This file is used to analyse the IFE granular and spinous dataset.

library(dplyr)
library(patchwork)
library(ggplot2)
library(ComplexHeatmap)

.libPaths()
## [1] "/usr/local/lib/R/library"

Preparation

In this section, we set the global settings of the analysis. We will store data there :

save_name = "ifegs"
out_dir = "."

We load the dataset :

sobj = readRDS(paste0(out_dir, "/", save_name, "_sobj.rds"))
sobj
## An object of class Seurat 
## 15051 features across 994 samples within 1 assay 
## Active assay: RNA (15051 features, 2000 variable features)
##  6 dimensional reductions calculated: RNA_pca, RNA_pca_22_tsne, RNA_pca_22_umap, harmony, harmony_22_umap, harmony_22_tsne

We load the sample information :

sample_info = readRDS(paste0(out_dir, "/../../1_metadata/hs_hd_sample_info.rds"))
project_names_oi = sample_info$project_name

graphics::pie(rep(1, nrow(sample_info)),
              col = sample_info$color,
              labels = sample_info$project_name)

Here are custom colors for each cell type :

color_markers = readRDS(paste0(out_dir, "/../../1_metadata/hs_hd_color_markers.rds"))

data.frame(cell_type = names(color_markers),
           color = unlist(color_markers)) %>%
  ggplot2::ggplot(., aes(x = cell_type, y = 0, fill = cell_type)) +
  ggplot2::geom_point(pch = 21, size = 5) +
  ggplot2::scale_fill_manual(values = unlist(color_markers), breaks = names(color_markers)) +
  ggplot2::theme_classic() +
  ggplot2::theme(legend.position = "none",
                 axis.line = element_blank(),
                 axis.title = element_blank(),
                 axis.ticks = element_blank(),
                 axis.text.y = element_blank(),
                 axis.text.x = element_text(angle = 30, hjust = 1))

This is the projection of interest :

name2D = "harmony_22_tsne"

We design a custom function to make the GSEA plot and a word cloud graph :

make_gsea_plot = function(gsea_results, gs_oi, fold_change, metric = "FC") {
  fold_change$metric = fold_change[, metric]
  
  plot_list = lapply(gs_oi, FUN = function(gene_set) {
    # Gene set content
    gs_content = gene_sets %>%
      dplyr::filter(gs_name == gene_set) %>%
      dplyr::pull(ensembl_gene) %>%
      unique()
    
    # Gene set size
    nb_genes = length(gs_content)
    
    # Enrichment metrics
    NES = gsea_results@result[gene_set, "NES"]
    p.adjust = gsea_results@result[gene_set, "p.adjust"] %>%
      round(., 4)
    qvalues = gsea_results@result[gene_set, "qvalues"]
    
    if (p.adjust > 0.05) {
      p.adjust = paste0("<span style='color:red;'>", p.adjust, "</span>")
    }
    
    my_subtitle = paste0("\nNES : ", round(NES, 2),
                         " | padj : ", p.adjust,
                         " | qval : ", round(qvalues, 4),
                         " | set size : ", nb_genes, " genes")
    
    # Size limits
    lower_FC = min(fold_change[gs_content, ]$metric, na.rm = TRUE)
    upper_FC = max(fold_change[gs_content, ]$metric, na.rm = TRUE)
    
    # Plot
    p = enrichplot::gseaplot2(x = gsea_results, geneSetID = gene_set) +
      ggplot2::labs(title = gene_set,
                    subtitle = my_subtitle) +
      ggplot2::theme(plot.title = element_text(hjust = 0.5, face = "bold",
                                               margin = ggplot2::margin(3, 3, 5, 3)),
                     plot.subtitle = ggtext::element_markdown(hjust = 0.5,
                                                              size = 10))
    
    wc = ggplot2::ggplot(fold_change[gs_content, ],
                         aes(label = gene_name, size = abs(metric), color = metric)) +
      ggwordcloud::geom_text_wordcloud_area(show.legend = TRUE) +
      ggplot2::scale_color_gradient2(
        name = metric,
        low = aquarius::color_cnv[1],
        mid = "gray70", midpoint = 0,
        high = aquarius::color_cnv[3]) +
      ggplot2::scale_size_area(max_size = 7) +
      ggplot2::theme_minimal() +
      ggplot2::guides(size = "none")
    
    return(list(p, wc))
  }) %>% unlist(., recursive = FALSE)
  
  return(plot_list)
}

Visualization

Gene expression

We visualize gene expression for some markers :

features = c("percent.mt", "nFeature_RNA", "TK1",
             "SPINK5", "KRT1", "KRTDAP")

plot_list = lapply(features, FUN = function(one_gene) {
  Seurat::FeaturePlot(sobj, features = one_gene,
                      reduction = name2D) +
    ggplot2::theme(aspect.ratio = 1) +
    ggplot2::scale_color_gradientn(colors = aquarius::color_gene) +
    Seurat::NoAxes()
})

patchwork::wrap_plots(plot_list, ncol = 3)

Clusters

We visualize clusters :

cluster_plot = Seurat::DimPlot(sobj, reduction = name2D, label = TRUE) +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1)
cluster_plot

Cluster annotation

KRT1 and KRTDAP being specifically expressed in IFE spinous, we make a cluster annotation:

sobj$cluster_type = case_when(
  sobj$seurat_clusters %in% c(5,6) ~ "IFE proliferative",
  sobj$seurat_clusters %in% c(1,2,3) ~ "IFE spinous",
  sobj$seurat_clusters %in% c(0,4) ~ "IFE granular",
  TRUE ~ "others") %>% # others: 8,9,7,0
  factor(., levels = c("IFE spinous", "IFE granular",
                       "IFE proliferative", "others"))

cluster_color = setNames(nm = c("IFE spinous", "IFE granular", "IFE proliferative", "others"),
                         c("steelblue4", "lightskyblue", "gray50", "gray10"))

We visualize the cluster annotation split by sample :

plot_list = aquarius::plot_split_dimred(sobj,
                                        reduction = name2D,
                                        split_by = "project_name",
                                        group_by = "cluster_type",
                                        split_color = setNames(sample_info$color,
                                                               nm = sample_info$project_name),
                                        group_color = cluster_color,
                                        bg_pt_size = 0.5, main_pt_size = 0.5)

plot_list[[length(plot_list) + 1]] = Seurat::DimPlot(sobj, reduction = name2D,
                                                     group.by = "cluster_type") +
  ggplot2::scale_color_manual(values = cluster_color,
                              breaks = names(cluster_color)) +
  Seurat::NoAxes() +
  ggplot2::theme(aspect.ratio = 1)

patchwork::wrap_plots(plot_list, ncol = 4) &
  Seurat::NoLegend()

Differential expression

We save the results in a list :

list_results = list()

We make over-representation analysis for each group of genes. We load gene sets from MSigDB :

gene_sets = aquarius::get_gene_sets(species = "Homo sapiens")
gene_sets = gene_sets$gene_sets

head(gene_sets)
## # A tibble: 6 x 16
##   gs_cat gs_subcat gs_name gene_symbol entrez_gene ensembl_gene human_gene_symb~
##   <chr>  <chr>     <chr>   <chr>             <int> <chr>        <chr>           
## 1 C5     GO:BP     GOBP_1~ AASDHPPT          60496 ENSG0000014~ AASDHPPT        
## 2 C5     GO:BP     GOBP_1~ ALDH1L1           10840 ENSG0000014~ ALDH1L1         
## 3 C5     GO:BP     GOBP_1~ ALDH1L2          160428 ENSG0000013~ ALDH1L2         
## 4 C5     GO:BP     GOBP_1~ MTHFD1             4522 ENSG0000010~ MTHFD1          
## 5 C5     GO:BP     GOBP_1~ MTHFD1L           25902 ENSG0000012~ MTHFD1L         
## 6 C5     GO:BP     GOBP_1~ MTHFD2L          441024 ENSG0000016~ MTHFD2L         
## # ... with 9 more variables: human_entrez_gene <int>, human_ensembl_gene <chr>,
## #   gs_id <chr>, gs_pmid <chr>, gs_geoid <chr>, gs_exact_source <chr>,
## #   gs_url <chr>, gs_description <chr>, category <chr>

How many gene sets ?

gene_sets[, c("gs_subcat", "gs_name")] %>%
  dplyr::distinct() %>%
  dplyr::pull(gs_subcat) %>%
  table() %>%
  as.data.frame.table() %>%
  `colnames<-`(c("Category", "Nb gene sets"))
##          Category Nb gene sets
## 1                           50
## 2         CP:KEGG          186
## 3          CP:PID          196
## 4     CP:REACTOME         1615
## 5 CP:WIKIPATHWAYS          664
## 6           GO:BP         7658
## 7           GO:CC         1006
## 8           GO:MF         1738

We get gene name and gene ID correspondence :

gene_corresp = sobj@assays[["RNA"]]@meta.features[, c("gene_name", "Ensembl_ID")] %>%
  `colnames<-`(c("NAME", "ID")) %>%
  dplyr::mutate(ID = as.character(ID))
rownames(gene_corresp) = gene_corresp$ID

head(gene_corresp)
##                       NAME              ID
## ENSG00000238009 AL627309.1 ENSG00000238009
## ENSG00000237491 AL669831.5 ENSG00000237491
## ENSG00000225880  LINC00115 ENSG00000225880
## ENSG00000230368     FAM41C ENSG00000230368
## ENSG00000230699 AL645608.3 ENSG00000230699
## ENSG00000187634     SAMD11 ENSG00000187634

Granular vs spinous

We perform a differential expression analysis between granular and spinous clusters.

group_name = "granular_vs_spinous"

Seurat::Idents(sobj) = sobj$cluster_type
table(Seurat::Idents(sobj))
## 
##       IFE spinous      IFE granular IFE proliferative            others 
##               350               260               153               231

We visualize these populations :

aquarius::plot_red_and_blue(sobj, reduction = name2D,
                            group1 = "IFE granular", group2 = "IFE spinous")

DE

We identify DE genes between IFE granular and IFE spinous :

mark = Seurat::FindMarkers(sobj, ident.1 = "IFE granular", ident.2 = "IFE spinous")

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::arrange(-avg_logFC, pct.1 - pct.2)

list_results[[group_name]]$mark = mark

dim(mark)
## [1] 132   5
head(mark, n = 20)
##                 p_val avg_logFC pct.1 pct.2    p_val_adj
## PTN      2.884716e-60 1.2330983 0.927 0.340 4.341786e-56
## LGALS1   7.228537e-19 1.0315635 0.592 0.274 1.087967e-14
## APOE     5.473262e-42 0.7711590 0.992 0.886 8.237807e-38
## CHI3L1   7.302950e-33 0.7452335 0.631 0.160 1.099167e-28
## C1QTNF12 4.219449e-42 0.7192155 0.781 0.254 6.350692e-38
## CYP27A1  1.879473e-51 0.7034252 0.758 0.157 2.828795e-47
## DAPK2    3.697558e-49 0.7027778 0.808 0.246 5.565195e-45
## CLDN1    1.697175e-30 0.6445342 0.881 0.551 2.554419e-26
## EFNB2    9.527752e-43 0.6315371 0.765 0.206 1.434022e-38
## CYP1B1   3.413979e-35 0.6183131 0.565 0.091 5.138379e-31
## ADGRL3   1.717088e-43 0.5942463 0.788 0.226 2.584389e-39
## S100A2   1.920555e-18 0.5641180 0.992 0.931 2.890627e-14
## NFIB     2.544404e-23 0.5558578 0.923 0.657 3.829582e-19
## MALAT1   1.119576e-15 0.5345120 0.985 0.869 1.685074e-11
## CD109    1.279586e-32 0.5342700 0.835 0.460 1.925904e-28
## ZFP36L2  3.209914e-23 0.5297392 0.958 0.734 4.831241e-19
## ACTN1    2.516650e-25 0.4620441 0.881 0.589 3.787809e-21
## S100A6   2.860886e-18 0.4473052 0.977 0.869 4.305920e-14
## XIST     1.069519e-12 0.4310408 0.704 0.423 1.609734e-08
## APOC1    7.124088e-24 0.4264331 0.712 0.306 1.072246e-19

Up in IFE granular

We explore enrichment in gene sets for HS population.

genes_of_interest = mark %>%
  dplyr::filter(avg_logFC > 0) %>%
  rownames()

enrichr_results = aquarius::run_enrichr(gene_names = genes_of_interest,
                                        gene_corresp = gene_corresp,
                                        gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                        make_plot = TRUE,
                                        plot_title = "Up-regulated in IFE granular compared to IFE spinous")

list_results[[group_name]]$enrichr_hs = enrichr_results$ego

enrichr_results$plot +
  ggplot2::theme(axis.text.y = element_text(size = 8))

Up in IFE spinous

We explore enrichment in gene sets for HD population.

genes_of_interest = mark %>%
  dplyr::filter(avg_logFC < 0) %>%
  rownames()

enrichr_results = aquarius::run_enrichr(gene_names = genes_of_interest,
                                        gene_corresp = gene_corresp,
                                        gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                        make_plot = TRUE,
                                        plot_title = "Down-regulated in IFE granular compared to IFE spinous")

list_results[[group_name]]$enrichr_hd = enrichr_results$ego

enrichr_results$plot +
  ggplot2::theme(axis.text.y = element_text(size = 8))

GSEA

We run a GSEA for all gene sets, from the full count matrix :

ranked_gene_list = aquarius::run_foldchange(Seurat::GetAssayData(sobj, assay = "RNA", slot = "counts"),
                                            group1 = colnames(sobj)[sobj@active.ident %in% "IFE granular"],
                                            group2 = colnames(sobj)[sobj@active.ident %in% "IFE spinous"])
names(ranked_gene_list) = gene_corresp$ID

gsea_results = aquarius::gsea_run(ranked_gene_list = ranked_gene_list,
                                  gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                  GSEA_p_val_thresh = 1)

list_results[[group_name]]$gsea = gsea_results

gsea_results@result %>%
  dplyr::filter(pvalue < 0.05) %>%
  dplyr::top_n(., n = 200, wt = abs(NES)) %>%
  dplyr::mutate(too_long = ifelse(nchar(ID) > 60, yes = TRUE, no = FALSE)) %>%
  dplyr::mutate(ID = stringr::str_sub(ID, end = 60)) %>%
  dplyr::mutate(ID = ifelse(too_long, yes = paste0(ID, "..."), no = ID)) %>%
  aquarius::gsea_plot(show_legend = TRUE) +
  ggplot2::labs(title = "GSEA using all genes (count matrix)") +
  ggplot2::theme(plot.title = element_text(size = 20))

We compute a fold change table to make a wordcloud :

fold_change = gene_corresp
colnames(fold_change) = c("gene_name", "ID")
rownames(fold_change) = fold_change$ID
fold_change$FC = ranked_gene_list[rownames(fold_change)]
fold_change$pct.1 = Seurat::GetAssayData(sobj, assay = "RNA",
                                         slot = "counts")[, sobj@active.ident == "IFE granular"] %>%
  apply(., MARGIN = 1, FUN = function(one_row) {
    return( mean(one_row != 0) )
  })
fold_change$pct.2 = Seurat::GetAssayData(sobj, assay = "RNA",
                                         slot = "counts")[, sobj@active.ident == "IFE spinous"] %>%
  apply(., MARGIN = 1, FUN = function(one_row) {
    return( mean(one_row != 0) )
  })
fold_change$FC_x_pct = ifelse(fold_change$FC > 0,
                              yes = fold_change$FC * fold_change$pct.1,
                              no = fold_change$FC * fold_change$pct.2)

dim(fold_change) ; head(fold_change)
## [1] 15051     6
##                  gene_name              ID         FC       pct.1       pct.2
## ENSG00000238009 AL627309.1 ENSG00000238009 -1.7523930 0.000000000 0.008571429
## ENSG00000237491 AL669831.5 ENSG00000237491 -0.4392351 0.146153846 0.145714286
## ENSG00000225880  LINC00115 ENSG00000225880 -0.4404490 0.057692308 0.077142857
## ENSG00000230368     FAM41C ENSG00000230368  0.4820722 0.065384615 0.042857143
## ENSG00000230699 AL645608.3 ENSG00000230699  0.2476070 0.019230769 0.014285714
## ENSG00000187634     SAMD11 ENSG00000187634  0.2476070 0.003846154 0.002857143
##                      FC_x_pct
## ENSG00000238009 -0.0150205116
## ENSG00000237491 -0.0640028336
## ENSG00000225880 -0.0339774952
## ENSG00000230368  0.0315201077
## ENSG00000230699  0.0047616727
## ENSG00000187634  0.0009523345

We make the gsea plot for some gene sets :

gs_oi = c("GOBP_KERATINIZATION",
          "GOCC_CORNIFIED_ENVELOPE",
          "GOBP_CELLULAR_RESPONSE_TO_COPPER_ION",
          "GOBP_CELLULAR_RESPONSE_TO_CADMIUM_ION",
          "REACTOME_RESPONSE_TO_METAL_IONS",
          "WP_TGFBETA_RECEPTOR_SIGNALING")

plot_list = make_gsea_plot(gsea_results, gs_oi, fold_change, "FC_x_pct")

patchwork::wrap_plots(plot_list, ncol = 2, widths = c(2,1.5))

We attribute score for each gene sets to all cells, to visualize the scores as violin plots.

for (one_gene_set in gs_oi) {
  new_column = paste0("score_", one_gene_set)
  
  # gene set content
  gs_content = gene_sets %>%
    dplyr::filter(gs_name == one_gene_set) %>%
    dplyr::pull(gene_symbol) %>%
    unlist()
  
  # add score
  sobj@meta.data[, new_column] = Seurat::AddModuleScore(sobj,
                                                        features = list(gs_content))$Cluster1
}

head(sobj@meta.data)
##                            orig.ident nCount_RNA nFeature_RNA log_nCount_RNA
## 2021_31_AAAGTGATCCTGTTGC-1    2021_31       2505          899       7.826443
## 2021_31_AACCACAAGGGCGAGA-1    2021_31      36505         5336      10.505615
## 2021_31_AAGCGAGTCGACATAC-1    2021_31      38606         5713      10.561629
## 2021_31_AATGCCAGTATCGAAA-1    2021_31      21053         4605       9.955843
## 2021_31_ACACGCGTCTAGTACG-1    2021_31       6138         1966       8.722580
## 2021_31_ACAGAAAGTTCTCCCA-1    2021_31      31947         5093      10.372240
##                            project_name sample_identifier sample_type
## 2021_31_AAAGTGATCCTGTTGC-1      2021_31              HS_1          HS
## 2021_31_AACCACAAGGGCGAGA-1      2021_31              HS_1          HS
## 2021_31_AAGCGAGTCGACATAC-1      2021_31              HS_1          HS
## 2021_31_AATGCCAGTATCGAAA-1      2021_31              HS_1          HS
## 2021_31_ACACGCGTCTAGTACG-1      2021_31              HS_1          HS
## 2021_31_ACAGAAAGTTCTCCCA-1      2021_31              HS_1          HS
##                            laboratory location Seurat.Phase cyclone.Phase
## 2021_31_AAAGTGATCCTGTTGC-1        Our axillary          G2M            G1
## 2021_31_AACCACAAGGGCGAGA-1        Our axillary            S            G1
## 2021_31_AAGCGAGTCGACATAC-1        Our axillary            S            G1
## 2021_31_AATGCCAGTATCGAAA-1        Our axillary           G1             S
## 2021_31_ACACGCGTCTAGTACG-1        Our axillary           G1            G1
## 2021_31_ACAGAAAGTTCTCCCA-1        Our axillary           G1            G1
##                            percent.mt percent.rb            cell_type
## 2021_31_AAAGTGATCCTGTTGC-1  10.774142   30.88587            IFE basal
## 2021_31_AACCACAAGGGCGAGA-1   4.364732   35.56955            IFE basal
## 2021_31_AAGCGAGTCGACATAC-1   4.020816   29.88815            sebocytes
## 2021_31_AATGCCAGTATCGAAA-1   4.317912   25.80783            IFE basal
## 2021_31_ACACGCGTCTAGTACG-1   0.228013   23.87622 IFE granular spinous
## 2021_31_ACAGAAAGTTCTCCCA-1   4.152065   29.18023 IFE granular spinous
##                            RNA_snn_res.0.9 seurat_clusters      cluster_type
## 2021_31_AAAGTGATCCTGTTGC-1               7               7            others
## 2021_31_AACCACAAGGGCGAGA-1               6               6 IFE proliferative
## 2021_31_AAGCGAGTCGACATAC-1               0               0      IFE granular
## 2021_31_AATGCCAGTATCGAAA-1               0               0      IFE granular
## 2021_31_ACACGCGTCTAGTACG-1               4               4      IFE granular
## 2021_31_ACAGAAAGTTCTCCCA-1               2               2       IFE spinous
##                            score_GOBP_KERATINIZATION
## 2021_31_AAAGTGATCCTGTTGC-1               -0.13545620
## 2021_31_AACCACAAGGGCGAGA-1               -0.05531981
## 2021_31_AAGCGAGTCGACATAC-1               -0.04239343
## 2021_31_AATGCCAGTATCGAAA-1               -0.03069908
## 2021_31_ACACGCGTCTAGTACG-1                0.03061087
## 2021_31_ACAGAAAGTTCTCCCA-1               -0.02291899
##                            score_GOCC_CORNIFIED_ENVELOPE
## 2021_31_AAAGTGATCCTGTTGC-1                   -0.12450299
## 2021_31_AACCACAAGGGCGAGA-1                   -0.14821611
## 2021_31_AAGCGAGTCGACATAC-1                   -0.19455109
## 2021_31_AATGCCAGTATCGAAA-1                    0.05711020
## 2021_31_ACACGCGTCTAGTACG-1                    0.49044861
## 2021_31_ACAGAAAGTTCTCCCA-1                    0.02982518
##                            score_GOBP_CELLULAR_RESPONSE_TO_COPPER_ION
## 2021_31_AAAGTGATCCTGTTGC-1                                 0.15733893
## 2021_31_AACCACAAGGGCGAGA-1                                -0.03086286
## 2021_31_AAGCGAGTCGACATAC-1                                -0.10995941
## 2021_31_AATGCCAGTATCGAAA-1                                 0.01772833
## 2021_31_ACACGCGTCTAGTACG-1                                -0.06047799
## 2021_31_ACAGAAAGTTCTCCCA-1                                -0.06338896
##                            score_GOBP_CELLULAR_RESPONSE_TO_CADMIUM_ION
## 2021_31_AAAGTGATCCTGTTGC-1                                -0.013775233
## 2021_31_AACCACAAGGGCGAGA-1                                 0.072102378
## 2021_31_AAGCGAGTCGACATAC-1                                -0.060180186
## 2021_31_AATGCCAGTATCGAAA-1                                 0.111538469
## 2021_31_ACACGCGTCTAGTACG-1                                -0.078435331
## 2021_31_ACAGAAAGTTCTCCCA-1                                 0.007335939
##                            score_REACTOME_RESPONSE_TO_METAL_IONS
## 2021_31_AAAGTGATCCTGTTGC-1                            0.17842370
## 2021_31_AACCACAAGGGCGAGA-1                           -0.11334618
## 2021_31_AAGCGAGTCGACATAC-1                           -0.12670757
## 2021_31_AATGCCAGTATCGAAA-1                            0.03215326
## 2021_31_ACACGCGTCTAGTACG-1                           -0.20175066
## 2021_31_ACAGAAAGTTCTCCCA-1                           -0.08283927
##                            score_WP_TGFBETA_RECEPTOR_SIGNALING
## 2021_31_AAAGTGATCCTGTTGC-1                        -0.038972501
## 2021_31_AACCACAAGGGCGAGA-1                         0.038135302
## 2021_31_AAGCGAGTCGACATAC-1                        -0.047914885
## 2021_31_AATGCCAGTATCGAAA-1                         0.056690016
## 2021_31_ACACGCGTCTAGTACG-1                         0.005909078
## 2021_31_ACAGAAAGTTCTCCCA-1                         0.018563038

We visualize the scores as violin plots :

plot_list = lapply(gs_oi, FUN = function(one_gs) {
  p = Seurat::VlnPlot(sobj, features = paste0("score_", one_gs),
                      group.by = "cluster_type",
                      split.by = "sample_type") +
    ggplot2::scale_fill_manual(values = c("#C55F40", "#2C78E6"),
                               breaks = c("HS", "HD")) +
    ggplot2::labs(title = one_gs) +
    ggplot2::theme(axis.title.x = element_blank())
  
  return(p)
})

patchwork::wrap_plots(plot_list, ncol = 2)

HS vs HD

We perform a differential expression analysis for all clusters, between HS patients (HS) and healthy donors (HD).

group_name = "HS_vs_HD"

Seurat::Idents(sobj) = sobj$sample_type
table(Seurat::Idents(sobj))
## 
##  HS  HD 
## 857 137

DE

We identify DE genes between HS and HD :

mark = Seurat::FindMarkers(sobj, ident.1 = "HS", ident.2 = "HD")

mark = mark %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::arrange(-avg_logFC, pct.1 - pct.2)

list_results[[group_name]]$mark = mark

dim(mark)
## [1] 125   5
head(mark, n = 20)
##                  p_val avg_logFC pct.1 pct.2    p_val_adj
## S100A7    1.707978e-26 1.5712922 0.797 0.314 2.570678e-22
## LGALS7B   2.619916e-20 1.4849381 0.875 0.774 3.943235e-16
## LGALS7    5.644298e-20 1.4848100 0.587 0.219 8.495234e-16
## S100A9    2.213672e-22 1.2245197 0.883 0.562 3.331798e-18
## SERPINB4  1.691713e-09 1.2032478 0.263 0.029 2.546198e-05
## KRT16     3.204700e-09 1.0306455 0.518 0.219 4.823395e-05
## MIF       5.485978e-23 0.8920172 0.667 0.321 8.256945e-19
## FABP5     8.993906e-15 0.8903571 0.918 0.759 1.353673e-10
## RPS26     3.180425e-47 0.8234142 0.982 0.993 4.786857e-43
## MTRNR2L8  4.560037e-16 0.8078924 0.840 0.774 6.863312e-12
## AKR1B10   6.070274e-28 0.7973171 0.569 0.036 9.136369e-24
## SERPINB3  2.661133e-08 0.7824037 0.219 0.015 4.005271e-04
## S100A8    1.038251e-16 0.7760133 0.887 0.613 1.562672e-12
## CRABP2    2.107146e-09 0.5757340 0.659 0.409 3.171465e-05
## MTRNR2L10 5.297609e-10 0.5584586 0.627 0.460 7.973432e-06
## HSPA8     2.017428e-14 0.4930622 0.882 0.752 3.036431e-10
## ARF5      1.640443e-21 0.4903324 0.516 0.080 2.469031e-17
## IFITM3    1.908740e-06 0.4180892 0.769 0.620 2.872845e-02
## MTRNR2L12 8.389870e-07 0.4153288 0.870 0.781 1.262759e-02
## S100A11   2.884911e-17 0.4150983 0.984 0.978 4.342079e-13

Up in HS

We explore enrichment in gene sets for HS population.

genes_of_interest = mark %>%
  dplyr::filter(avg_logFC > 0) %>%
  rownames()

enrichr_results = aquarius::run_enrichr(gene_names = genes_of_interest,
                                        gene_corresp = gene_corresp,
                                        gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                        make_plot = TRUE,
                                        plot_title = "Up-regulated in HS compared to HD")

list_results[[group_name]]$enrichr_hs = enrichr_results$ego

enrichr_results$plot +
  ggplot2::theme(axis.text.y = element_text(size = 8))

Up in HD

We explore enrichment in gene sets for HD population.

genes_of_interest = mark %>%
  dplyr::filter(avg_logFC < 0) %>%
  rownames()

enrichr_results = aquarius::run_enrichr(gene_names = genes_of_interest,
                                        gene_corresp = gene_corresp,
                                        gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                        make_plot = TRUE,
                                        plot_title = "Down-regulated in HS compared to HD")

list_results[[group_name]]$enrichr_hd = enrichr_results$ego

enrichr_results$plot +
  ggplot2::theme(axis.text.y = element_text(size = 8))

GSEA

We run a GSEA for all gene sets, from the full count matrix :

ranked_gene_list = aquarius::run_foldchange(Seurat::GetAssayData(sobj, assay = "RNA", slot = "counts"),
                                            group1 = colnames(sobj)[sobj@active.ident %in% "HS"],
                                            group2 = colnames(sobj)[sobj@active.ident %in% "HD"])
names(ranked_gene_list) = gene_corresp$ID

gsea_results = aquarius::gsea_run(ranked_gene_list = ranked_gene_list,
                                  gene_sets = gene_sets[, c("gs_name", "ensembl_gene")],
                                  GSEA_p_val_thresh = 1)

list_results[[group_name]]$gsea = gsea_results

gsea_results@result %>%
  dplyr::filter(pvalue < 0.05) %>%
  dplyr::top_n(., n = 200, wt = abs(NES)) %>%
  dplyr::mutate(too_long = ifelse(nchar(ID) > 60, yes = TRUE, no = FALSE)) %>%
  dplyr::mutate(ID = stringr::str_sub(ID, end = 60)) %>%
  dplyr::mutate(ID = ifelse(too_long, yes = paste0(ID, "..."), no = ID)) %>%
  aquarius::gsea_plot(show_legend = TRUE) +
  ggplot2::labs(title = "GSEA using all genes (count matrix)") +
  ggplot2::theme(plot.title = element_text(size = 20))

We compute a fold change table to make a wordcloud :

fold_change = gene_corresp
colnames(fold_change) = c("gene_name", "ID")
rownames(fold_change) = fold_change$ID
fold_change$FC = ranked_gene_list[rownames(fold_change)]
fold_change$pct.1 = Seurat::GetAssayData(sobj, assay = "RNA", slot = "counts")[, sobj@active.ident == "HS"] %>%
  apply(., MARGIN = 1, FUN = function(one_row) {
    return( mean(one_row != 0) )
  })
fold_change$pct.2 = Seurat::GetAssayData(sobj, assay = "RNA", slot = "counts")[, sobj@active.ident == "HD"] %>%
  apply(., MARGIN = 1, FUN = function(one_row) {
    return( mean(one_row != 0) )
  })
fold_change$FC_x_pct = ifelse(fold_change$FC > 0,
                              yes = fold_change$FC * fold_change$pct.1,
                              no = fold_change$FC * fold_change$pct.2)

dim(fold_change) ; head(fold_change)
## [1] 15051     6
##                  gene_name              ID          FC       pct.1      pct.2
## ENSG00000238009 AL627309.1 ENSG00000238009  0.03273644 0.010501750 0.00000000
## ENSG00000237491 AL669831.5 ENSG00000237491  0.17957783 0.149358226 0.08029197
## ENSG00000225880  LINC00115 ENSG00000225880  0.47634309 0.071178530 0.02919708
## ENSG00000230368     FAM41C ENSG00000230368  1.14821366 0.066511085 0.01459854
## ENSG00000230699 AL645608.3 ENSG00000230699 -0.11926665 0.019836639 0.00729927
## ENSG00000187634     SAMD11 ENSG00000187634 -3.45911666 0.005834306 0.03649635
##                      FC_x_pct
## ENSG00000238009  0.0003437899
## ENSG00000237491  0.0268214259
## ENSG00000225880  0.0339054009
## ENSG00000230368  0.0763689364
## ENSG00000230699 -0.0008705595
## ENSG00000187634 -0.1262451334

We make the gsea plot for some gene sets :

gs_oi = c("HALLMARK_INTERFERON_ALPHA_RESPONSE",
          "HALLMARK_INTERFERON_GAMMA_RESPONSE",
          "REACTOME_KERATINIZATION",
          "GOBP_KERATINIZATION",
          "GOBP_MACROPHAGE_CYTOKINE_PRODUCTION",
          "GOCC_MHC_PROTEIN_COMPLEX",
          "KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION")

plot_list = make_gsea_plot(gsea_results, gs_oi, fold_change, "FC_x_pct")

patchwork::wrap_plots(plot_list, ncol = 2, widths = c(2,1.5))

We attribute score for each gene sets to all cells, to visualize the scores as violin plots.

for (one_gene_set in gs_oi) {
  new_column = paste0("score_", one_gene_set)
  
  # gene set content
  gs_content = gene_sets %>%
    dplyr::filter(gs_name == one_gene_set) %>%
    dplyr::pull(gene_symbol) %>%
    unlist()
  
  # add score
  sobj@meta.data[, new_column] = Seurat::AddModuleScore(sobj,
                                                        features = list(gs_content))$Cluster1
}

head(sobj@meta.data)
##                            orig.ident nCount_RNA nFeature_RNA log_nCount_RNA
## 2021_31_AAAGTGATCCTGTTGC-1    2021_31       2505          899       7.826443
## 2021_31_AACCACAAGGGCGAGA-1    2021_31      36505         5336      10.505615
## 2021_31_AAGCGAGTCGACATAC-1    2021_31      38606         5713      10.561629
## 2021_31_AATGCCAGTATCGAAA-1    2021_31      21053         4605       9.955843
## 2021_31_ACACGCGTCTAGTACG-1    2021_31       6138         1966       8.722580
## 2021_31_ACAGAAAGTTCTCCCA-1    2021_31      31947         5093      10.372240
##                            project_name sample_identifier sample_type
## 2021_31_AAAGTGATCCTGTTGC-1      2021_31              HS_1          HS
## 2021_31_AACCACAAGGGCGAGA-1      2021_31              HS_1          HS
## 2021_31_AAGCGAGTCGACATAC-1      2021_31              HS_1          HS
## 2021_31_AATGCCAGTATCGAAA-1      2021_31              HS_1          HS
## 2021_31_ACACGCGTCTAGTACG-1      2021_31              HS_1          HS
## 2021_31_ACAGAAAGTTCTCCCA-1      2021_31              HS_1          HS
##                            laboratory location Seurat.Phase cyclone.Phase
## 2021_31_AAAGTGATCCTGTTGC-1        Our axillary          G2M            G1
## 2021_31_AACCACAAGGGCGAGA-1        Our axillary            S            G1
## 2021_31_AAGCGAGTCGACATAC-1        Our axillary            S            G1
## 2021_31_AATGCCAGTATCGAAA-1        Our axillary           G1             S
## 2021_31_ACACGCGTCTAGTACG-1        Our axillary           G1            G1
## 2021_31_ACAGAAAGTTCTCCCA-1        Our axillary           G1            G1
##                            percent.mt percent.rb            cell_type
## 2021_31_AAAGTGATCCTGTTGC-1  10.774142   30.88587            IFE basal
## 2021_31_AACCACAAGGGCGAGA-1   4.364732   35.56955            IFE basal
## 2021_31_AAGCGAGTCGACATAC-1   4.020816   29.88815            sebocytes
## 2021_31_AATGCCAGTATCGAAA-1   4.317912   25.80783            IFE basal
## 2021_31_ACACGCGTCTAGTACG-1   0.228013   23.87622 IFE granular spinous
## 2021_31_ACAGAAAGTTCTCCCA-1   4.152065   29.18023 IFE granular spinous
##                            RNA_snn_res.0.9 seurat_clusters      cluster_type
## 2021_31_AAAGTGATCCTGTTGC-1               7               7            others
## 2021_31_AACCACAAGGGCGAGA-1               6               6 IFE proliferative
## 2021_31_AAGCGAGTCGACATAC-1               0               0      IFE granular
## 2021_31_AATGCCAGTATCGAAA-1               0               0      IFE granular
## 2021_31_ACACGCGTCTAGTACG-1               4               4      IFE granular
## 2021_31_ACAGAAAGTTCTCCCA-1               2               2       IFE spinous
##                            score_GOBP_KERATINIZATION
## 2021_31_AAAGTGATCCTGTTGC-1               -0.13545620
## 2021_31_AACCACAAGGGCGAGA-1               -0.05531981
## 2021_31_AAGCGAGTCGACATAC-1               -0.04239343
## 2021_31_AATGCCAGTATCGAAA-1               -0.03069908
## 2021_31_ACACGCGTCTAGTACG-1                0.03061087
## 2021_31_ACAGAAAGTTCTCCCA-1               -0.02291899
##                            score_GOCC_CORNIFIED_ENVELOPE
## 2021_31_AAAGTGATCCTGTTGC-1                   -0.12450299
## 2021_31_AACCACAAGGGCGAGA-1                   -0.14821611
## 2021_31_AAGCGAGTCGACATAC-1                   -0.19455109
## 2021_31_AATGCCAGTATCGAAA-1                    0.05711020
## 2021_31_ACACGCGTCTAGTACG-1                    0.49044861
## 2021_31_ACAGAAAGTTCTCCCA-1                    0.02982518
##                            score_GOBP_CELLULAR_RESPONSE_TO_COPPER_ION
## 2021_31_AAAGTGATCCTGTTGC-1                                 0.15733893
## 2021_31_AACCACAAGGGCGAGA-1                                -0.03086286
## 2021_31_AAGCGAGTCGACATAC-1                                -0.10995941
## 2021_31_AATGCCAGTATCGAAA-1                                 0.01772833
## 2021_31_ACACGCGTCTAGTACG-1                                -0.06047799
## 2021_31_ACAGAAAGTTCTCCCA-1                                -0.06338896
##                            score_GOBP_CELLULAR_RESPONSE_TO_CADMIUM_ION
## 2021_31_AAAGTGATCCTGTTGC-1                                -0.013775233
## 2021_31_AACCACAAGGGCGAGA-1                                 0.072102378
## 2021_31_AAGCGAGTCGACATAC-1                                -0.060180186
## 2021_31_AATGCCAGTATCGAAA-1                                 0.111538469
## 2021_31_ACACGCGTCTAGTACG-1                                -0.078435331
## 2021_31_ACAGAAAGTTCTCCCA-1                                 0.007335939
##                            score_REACTOME_RESPONSE_TO_METAL_IONS
## 2021_31_AAAGTGATCCTGTTGC-1                            0.17842370
## 2021_31_AACCACAAGGGCGAGA-1                           -0.11334618
## 2021_31_AAGCGAGTCGACATAC-1                           -0.12670757
## 2021_31_AATGCCAGTATCGAAA-1                            0.03215326
## 2021_31_ACACGCGTCTAGTACG-1                           -0.20175066
## 2021_31_ACAGAAAGTTCTCCCA-1                           -0.08283927
##                            score_WP_TGFBETA_RECEPTOR_SIGNALING
## 2021_31_AAAGTGATCCTGTTGC-1                        -0.038972501
## 2021_31_AACCACAAGGGCGAGA-1                         0.038135302
## 2021_31_AAGCGAGTCGACATAC-1                        -0.047914885
## 2021_31_AATGCCAGTATCGAAA-1                         0.056690016
## 2021_31_ACACGCGTCTAGTACG-1                         0.005909078
## 2021_31_ACAGAAAGTTCTCCCA-1                         0.018563038
##                            score_HALLMARK_INTERFERON_ALPHA_RESPONSE
## 2021_31_AAAGTGATCCTGTTGC-1                              -0.01163969
## 2021_31_AACCACAAGGGCGAGA-1                               0.04052888
## 2021_31_AAGCGAGTCGACATAC-1                               0.02563452
## 2021_31_AATGCCAGTATCGAAA-1                               0.07830894
## 2021_31_ACACGCGTCTAGTACG-1                              -0.07371936
## 2021_31_ACAGAAAGTTCTCCCA-1                               0.05225323
##                            score_HALLMARK_INTERFERON_GAMMA_RESPONSE
## 2021_31_AAAGTGATCCTGTTGC-1                              -0.03163757
## 2021_31_AACCACAAGGGCGAGA-1                               0.01897797
## 2021_31_AAGCGAGTCGACATAC-1                               0.01765398
## 2021_31_AATGCCAGTATCGAAA-1                               0.04969244
## 2021_31_ACACGCGTCTAGTACG-1                              -0.04287433
## 2021_31_ACAGAAAGTTCTCCCA-1                               0.02979381
##                            score_REACTOME_KERATINIZATION
## 2021_31_AAAGTGATCCTGTTGC-1                  -0.004632692
## 2021_31_AACCACAAGGGCGAGA-1                   0.057549695
## 2021_31_AAGCGAGTCGACATAC-1                   0.066181820
## 2021_31_AATGCCAGTATCGAAA-1                   0.157580089
## 2021_31_ACACGCGTCTAGTACG-1                   0.407402733
## 2021_31_ACAGAAAGTTCTCCCA-1                   0.198902298
##                            score_GOBP_MACROPHAGE_CYTOKINE_PRODUCTION
## 2021_31_AAAGTGATCCTGTTGC-1                               -0.06397283
## 2021_31_AACCACAAGGGCGAGA-1                                0.09441426
## 2021_31_AAGCGAGTCGACATAC-1                                0.05652529
## 2021_31_AATGCCAGTATCGAAA-1                                0.05181899
## 2021_31_ACACGCGTCTAGTACG-1                                0.03751105
## 2021_31_ACAGAAAGTTCTCCCA-1                                0.04375760
##                            score_GOCC_MHC_PROTEIN_COMPLEX
## 2021_31_AAAGTGATCCTGTTGC-1                    -0.01578330
## 2021_31_AACCACAAGGGCGAGA-1                     0.36108395
## 2021_31_AAGCGAGTCGACATAC-1                     0.16194305
## 2021_31_AATGCCAGTATCGAAA-1                     0.39295972
## 2021_31_ACACGCGTCTAGTACG-1                     0.03782853
## 2021_31_ACAGAAAGTTCTCCCA-1                     0.34138984
##                            score_KEGG_ANTIGEN_PROCESSING_AND_PRESENTATION
## 2021_31_AAAGTGATCCTGTTGC-1                                     0.06097775
## 2021_31_AACCACAAGGGCGAGA-1                                     0.20508977
## 2021_31_AAGCGAGTCGACATAC-1                                     0.24957622
## 2021_31_AATGCCAGTATCGAAA-1                                     0.26696440
## 2021_31_ACACGCGTCTAGTACG-1                                     0.09250680
## 2021_31_ACAGAAAGTTCTCCCA-1                                     0.17942810

We visualize the scores as violin plots :

plot_list = lapply(gs_oi, FUN = function(one_gs) {
  p = Seurat::VlnPlot(sobj, features = paste0("score_", one_gene_set),
                      group.by = "cluster_type",
                      split.by = "sample_type") +
    ggplot2::scale_fill_manual(values = c("#C55F40", "#2C78E6"),
                               breaks = c("HS", "HD")) +
    ggplot2::labs(title = one_gs) +
    ggplot2::theme(axis.title.x = element_blank())
  
  return(p)
})

patchwork::wrap_plots(plot_list, ncol = 2)

Visualisation

We represent differentially expressed genes. First, we extract some DE genes :

features_oi = lapply(list_results, `[[`, "mark") %>%
  do.call(rbind.data.frame, .) %>%
  dplyr::filter(p_val_adj < 0.05) %>%
  dplyr::filter(pct.1 - pct.2 > 0.2 | abs(avg_logFC) > 1) %>%
  rownames() %>%
  stringr::str_split(., pattern = "\\.") %>%
  lapply(., `[[`, 2) %>%
  unlist() %>%
  unique()

length(features_oi)
## [1] 95

We prepare the scaled expression matrix :

mat_expression = Seurat::GetAssayData(sobj, assay = "RNA", slot = "data")[features_oi, ]
mat_expression = Matrix::t(mat_expression)
mat_expression = dynutils::scale_quantile(mat_expression) # between 0 and 1
mat_expression = Matrix::t(mat_expression)
mat_expression = as.matrix(mat_expression) # not sparse

dim(mat_expression)
## [1]  95 994

We prepare the heatmap annotation :

ha_top = ComplexHeatmap::HeatmapAnnotation(
  sample_type = sobj$sample_type,
  cluster_type = sobj$cluster_type,
  cluster = sobj$seurat_clusters,
  col = list(sample_type = setNames(nm = c("HS", "HD"),
                                    c("#C55F40", "#2C78E6")),
             cluster_type = cluster_color,
             cluster = setNames(nm = levels(sobj$seurat_clusters),
                                aquarius::gg_color_hue(length(levels(sobj$seurat_clusters))))))

And the heatmap :

ht = ComplexHeatmap::Heatmap(mat_expression,
                             col = aquarius::color_cnv,
                             # Annotation
                             top_annotation = ha_top,
                             # Grouping
                             column_order = sobj@meta.data %>%
                               dplyr::arrange(cluster_type, sample_type, seurat_clusters) %>%
                               rownames(),
                             column_title = NULL,
                             cluster_rows = FALSE,
                             cluster_columns = FALSE,
                             show_column_names = FALSE,
                             # Visual aspect
                             show_heatmap_legend = TRUE,
                             border = TRUE)

ComplexHeatmap::draw(ht,
                     merge_legend = TRUE,
                     heatmap_legend_side = "bottom",
                     annotation_legend_side = "bottom")

Save

We save the list of results :

saveRDS(list_results, file = paste0(out_dir, "/", save_name, "_list_results.rds"))

We save the Seurat object with the new annotation :

saveRDS(sobj, file = paste0(out_dir, "/", save_name, "_sobj_annotated.rds"))

R Session

show
## R version 3.6.3 (2020-02-29)
## Platform: x86_64-pc-linux-gnu (64-bit)
## Running under: Ubuntu 20.04.6 LTS
## 
## Matrix products: default
## BLAS:   /usr/local/lib/R/lib/libRblas.so
## LAPACK: /usr/local/lib/R/lib/libRlapack.so
## 
## locale:
## [1] C
## 
## attached base packages:
## [1] grid      stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
## [1] ComplexHeatmap_2.14.0 ggplot2_3.3.5         patchwork_1.1.2      
## [4] dplyr_1.0.7          
## 
## loaded via a namespace (and not attached):
##   [1] softImpute_1.4              graphlayouts_0.7.0         
##   [3] pbapply_1.4-2               lattice_0.20-41            
##   [5] haven_2.3.1                 vctrs_0.3.8                
##   [7] usethis_2.0.1               dynwrap_1.2.1              
##   [9] blob_1.2.1                  survival_3.2-13            
##  [11] prodlim_2019.11.13          dynutils_1.0.5             
##  [13] later_1.3.0                 DBI_1.1.1                  
##  [15] R.utils_2.11.0              SingleCellExperiment_1.8.0 
##  [17] rappdirs_0.3.3              uwot_0.1.8                 
##  [19] dqrng_0.2.1                 jpeg_0.1-8.1               
##  [21] zlibbioc_1.32.0             pspline_1.0-18             
##  [23] pcaMethods_1.78.0           mvtnorm_1.1-1              
##  [25] htmlwidgets_1.5.4           GlobalOptions_0.1.2        
##  [27] future_1.22.1               UpSetR_1.4.0               
##  [29] laeken_0.5.2                leiden_0.3.3               
##  [31] clustree_0.4.3              parallel_3.6.3             
##  [33] scater_1.14.6               irlba_2.3.3                
##  [35] markdown_1.1                DEoptimR_1.0-9             
##  [37] tidygraph_1.1.2             Rcpp_1.0.9                 
##  [39] readr_2.0.2                 KernSmooth_2.23-17         
##  [41] carrier_0.1.0               promises_1.1.0             
##  [43] gdata_2.18.0                DelayedArray_0.12.3        
##  [45] limma_3.42.2                graph_1.64.0               
##  [47] RcppParallel_5.1.4          Hmisc_4.4-0                
##  [49] fs_1.5.2                    RSpectra_0.16-0            
##  [51] fastmatch_1.1-0             ranger_0.12.1              
##  [53] digest_0.6.25               png_0.1-7                  
##  [55] sctransform_0.2.1           cowplot_1.0.0              
##  [57] DOSE_3.12.0                 here_1.0.1                 
##  [59] TInGa_0.0.0.9000            ggraph_2.0.3               
##  [61] pkgconfig_2.0.3             GO.db_3.10.0               
##  [63] DelayedMatrixStats_1.8.0    gower_0.2.1                
##  [65] ggbeeswarm_0.6.0            iterators_1.0.12           
##  [67] DropletUtils_1.6.1          reticulate_1.26            
##  [69] clusterProfiler_3.14.3      SummarizedExperiment_1.16.1
##  [71] circlize_0.4.15             beeswarm_0.4.0             
##  [73] GetoptLong_1.0.5            xfun_0.35                  
##  [75] bslib_0.3.1                 zoo_1.8-10                 
##  [77] tidyselect_1.1.0            reshape2_1.4.4             
##  [79] purrr_0.3.4                 ica_1.0-2                  
##  [81] pcaPP_1.9-73                viridisLite_0.3.0          
##  [83] rtracklayer_1.46.0          rlang_1.0.2                
##  [85] hexbin_1.28.1               jquerylib_0.1.4            
##  [87] dyneval_0.9.9               glue_1.4.2                 
##  [89] RColorBrewer_1.1-2          matrixStats_0.56.0         
##  [91] stringr_1.4.0               lava_1.6.7                 
##  [93] europepmc_0.3               DESeq2_1.26.0              
##  [95] recipes_0.1.17              labeling_0.3               
##  [97] httpuv_1.5.2                class_7.3-17               
##  [99] BiocNeighbors_1.4.2         DO.db_2.9                  
## [101] annotate_1.64.0             jsonlite_1.7.2             
## [103] XVector_0.26.0              bit_4.0.4                  
## [105] mime_0.9                    aquarius_0.1.5             
## [107] Rsamtools_2.2.3             gridExtra_2.3              
## [109] gplots_3.0.3                stringi_1.4.6              
## [111] processx_3.5.2              gsl_2.1-6                  
## [113] bitops_1.0-6                cli_3.0.1                  
## [115] batchelor_1.2.4             RSQLite_2.2.0              
## [117] randomForest_4.6-14         tidyr_1.1.4                
## [119] data.table_1.14.2           rstudioapi_0.13            
## [121] org.Mm.eg.db_3.10.0         GenomicAlignments_1.22.1   
## [123] nlme_3.1-147                qvalue_2.18.0              
## [125] scran_1.14.6                locfit_1.5-9.4             
## [127] scDblFinder_1.1.8           listenv_0.8.0              
## [129] ggthemes_4.2.4              gridGraphics_0.5-0         
## [131] R.oo_1.24.0                 dbplyr_1.4.4               
## [133] BiocGenerics_0.32.0         TTR_0.24.2                 
## [135] readxl_1.3.1                lifecycle_1.0.1            
## [137] timeDate_3043.102           ggpattern_0.3.1            
## [139] munsell_0.5.0               cellranger_1.1.0           
## [141] R.methodsS3_1.8.1           proxyC_0.1.5               
## [143] visNetwork_2.0.9            caTools_1.18.0             
## [145] codetools_0.2-16            ggwordcloud_0.5.0          
## [147] Biobase_2.46.0              GenomeInfoDb_1.22.1        
## [149] vipor_0.4.5                 lmtest_0.9-38              
## [151] msigdbr_7.5.1               htmlTable_1.13.3           
## [153] triebeard_0.3.0             lsei_1.2-0                 
## [155] xtable_1.8-4                ROCR_1.0-7                 
## [157] BiocManager_1.30.10         scatterplot3d_0.3-41       
## [159] abind_1.4-5                 farver_2.0.3               
## [161] parallelly_1.28.1           RANN_2.6.1                 
## [163] askpass_1.1                 GenomicRanges_1.38.0       
## [165] RcppAnnoy_0.0.16            tibble_3.1.5               
## [167] ggdendro_0.1-20             cluster_2.1.0              
## [169] future.apply_1.5.0          Seurat_3.1.5               
## [171] dendextend_1.15.1           Matrix_1.3-2               
## [173] ellipsis_0.3.2              prettyunits_1.1.1          
## [175] lubridate_1.7.9             ggridges_0.5.2             
## [177] igraph_1.2.5                RcppEigen_0.3.3.7.0        
## [179] fgsea_1.12.0                remotes_2.4.2              
## [181] scBFA_1.0.0                 destiny_3.0.1              
## [183] VIM_6.1.1                   testthat_3.1.0             
## [185] htmltools_0.5.2             BiocFileCache_1.10.2       
## [187] yaml_2.2.1                  utf8_1.1.4                 
## [189] plotly_4.9.2.1              XML_3.99-0.3               
## [191] ModelMetrics_1.2.2.2        e1071_1.7-3                
## [193] foreign_0.8-76              withr_2.5.0                
## [195] fitdistrplus_1.0-14         BiocParallel_1.20.1        
## [197] xgboost_1.4.1.1             bit64_4.0.5                
## [199] foreach_1.5.0               robustbase_0.93-9          
## [201] Biostrings_2.54.0           GOSemSim_2.13.1            
## [203] rsvd_1.0.3                  memoise_2.0.0              
## [205] evaluate_0.18               forcats_0.5.0              
## [207] rio_0.5.16                  geneplotter_1.64.0         
## [209] tzdb_0.1.2                  caret_6.0-86               
## [211] ps_1.6.0                    DiagrammeR_1.0.6.1         
## [213] curl_4.3                    fdrtool_1.2.15             
## [215] fansi_0.4.1                 highr_0.8                  
## [217] urltools_1.7.3              xts_0.12.1                 
## [219] GSEABase_1.48.0             acepack_1.4.1              
## [221] edgeR_3.28.1                checkmate_2.0.0            
## [223] scds_1.2.0                  cachem_1.0.6               
## [225] npsurv_0.4-0                babelgene_22.3             
## [227] rjson_0.2.20                openxlsx_4.1.5             
## [229] ggrepel_0.9.1               clue_0.3-60                
## [231] rprojroot_2.0.2             stabledist_0.7-1           
## [233] tools_3.6.3                 sass_0.4.0                 
## [235] nichenetr_1.1.1             magrittr_2.0.1             
## [237] RCurl_1.98-1.2              proxy_0.4-24               
## [239] car_3.0-11                  ape_5.3                    
## [241] ggplotify_0.0.5             xml2_1.3.2                 
## [243] httr_1.4.2                  assertthat_0.2.1           
## [245] rmarkdown_2.18              boot_1.3-25                
## [247] globals_0.14.0              R6_2.4.1                   
## [249] Rhdf5lib_1.8.0              nnet_7.3-14                
## [251] RcppHNSW_0.2.0              progress_1.2.2             
## [253] genefilter_1.68.0           statmod_1.4.34             
## [255] gtools_3.8.2                shape_1.4.6                
## [257] HDF5Array_1.14.4            BiocSingular_1.2.2         
## [259] rhdf5_2.30.1                splines_3.6.3              
## [261] AUCell_1.8.0                carData_3.0-4              
## [263] colorspace_1.4-1            generics_0.1.0             
## [265] stats4_3.6.3                base64enc_0.1-3            
## [267] dynfeature_1.0.0            smoother_1.1               
## [269] gridtext_0.1.1              pillar_1.6.3               
## [271] tweenr_1.0.1                sp_1.4-1                   
## [273] ggplot.multistats_1.0.0     rvcheck_0.1.8              
## [275] GenomeInfoDbData_1.2.2      plyr_1.8.6                 
## [277] gtable_0.3.0                zip_2.2.0                  
## [279] knitr_1.41                  latticeExtra_0.6-29        
## [281] biomaRt_2.42.1              IRanges_2.20.2             
## [283] fastmap_1.1.0               ADGofTest_0.3              
## [285] copula_1.0-0                doParallel_1.0.15          
## [287] AnnotationDbi_1.48.0        vcd_1.4-8                  
## [289] babelwhale_1.0.1            openssl_1.4.1              
## [291] scales_1.1.1                backports_1.2.1            
## [293] S4Vectors_0.24.4            ipred_0.9-12               
## [295] enrichplot_1.6.1            hms_1.1.1                  
## [297] ggforce_0.3.1               Rtsne_0.15                 
## [299] shiny_1.7.1                 numDeriv_2016.8-1.1        
## [301] polyclip_1.10-0             lazyeval_0.2.2             
## [303] Formula_1.2-3               tsne_0.1-3                 
## [305] crayon_1.3.4                MASS_7.3-54                
## [307] pROC_1.16.2                 viridis_0.5.1              
## [309] dynparam_1.0.0              rpart_4.1-15               
## [311] zinbwave_1.8.0              compiler_3.6.3             
## [313] ggtext_0.1.0
LS0tCnRpdGxlOiAiSFMgcHJvamVjdCIKc3VidGl0bGU6ICJab29tIGluIElGRSBncmFudWxhciBhbmQgc3Bpbm91cyIKYXV0aG9yOiAiQXVkcmV5IgpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclWS0lbS0lZCcpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCi0tLQoKPHN0eWxlPgpib2R5IHsKdGV4dC1hbGlnbjoganVzdGlmeX0KPC9zdHlsZT4KCjwhLS0gQXV0b21hdGljYWxseSBjb21wdXRlcyBhbmQgcHJpbnRzIGluIHRoZSBvdXRwdXQgdGhlIHJ1bm5pbmcgdGltZSBmb3IgYW55IGNvZGUgY2h1bmsgLS0+CmBgYHtyLCBlY2hvPUZBTFNFfQojIGh0dHBzOi8vZ2l0aHViLmNvbS9yc3R1ZGlvL3JtYXJrZG93bi9pc3N1ZXMvMTQ1Mwpob29rcyA9IGtuaXRyOjprbml0X2hvb2tzJGdldCgpCmhvb2tfZm9sZGFibGUgPSBmdW5jdGlvbih0eXBlKSB7CiAgZm9yY2UodHlwZSkKICBmdW5jdGlvbih4LCBvcHRpb25zKSB7CiAgICByZXMgPSBob29rc1tbdHlwZV1dKHgsIG9wdGlvbnMpCiAgICAKICAgIGlmIChpc0ZBTFNFKG9wdGlvbnNbW3Bhc3RlMCgiZm9sZF8iLCB0eXBlKV1dKSkgcmV0dXJuKHJlcykKICAgIAogICAgcGFzdGUwKAogICAgICAiPGRldGFpbHM+PHN1bW1hcnk+IiwgInNob3ciLCAiPC9zdW1tYXJ5PlxuXG4iLAogICAgICByZXMsCiAgICAgICJcblxuPC9kZXRhaWxzPiIKICAgICkKICB9Cn0Ka25pdHI6OmtuaXRfaG9va3Mkc2V0KAogIG91dHB1dCA9IGhvb2tfZm9sZGFibGUoIm91dHB1dCIpLAogIHBsb3QgPSBob29rX2ZvbGRhYmxlKCJwbG90IiksCiAgdGltZV9pdCA9IGxvY2FsKHsKICAgIG5vdyA9IE5VTEwKICAgIGZ1bmN0aW9uKGJlZm9yZSwgb3B0aW9ucykgewogICAgICBpZiAob3B0aW9ucyR0aW1lX2l0KSB7CiAgICAgICAgaWYgKGJlZm9yZSkgewogICAgICAgICAgbm93IDw9IFN5cy50aW1lKCkKICAgICAgICB9IGVsc2UgewogICAgICAgICAgcmVzID0gZGlmZnRpbWUoU3lzLnRpbWUoKSwgbm93LCB1bml0cyA9ICJzZWNzIikKICAgICAgICAgIHBhc3RlKCIoVGltZSB0byBydW4gOiIsIHJvdW5kKHJlcywgZGlnaXRzID0gMiksICJzKSIpCiAgICAgICAgfQogICAgICB9CiAgICB9CiAgfSkKKQpgYGAKCjwhLS0gU2V0IGRlZmF1bHQgcGFyYW1ldGVycyBmb3IgYWxsIGNodW5rcyAtLT4KYGBge3IsIHNldHVwLCBpbmNsdWRlID0gRkFMU0V9CnNldC5zZWVkKDEzMzdMKQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICMgZGlzcGxheSBjb2RlCiAgICAgICAgICAgICAgICAgICAgICAjIGRpc3BsYXkgY2h1bmsgb3V0cHV0CiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBmb2xkX291dHB1dCA9IEZBTFNFLCAjIHVzZWZ1bGwgZm9yIHNlc3Npb25JbmZvKCkKICAgICAgICAgICAgICAgICAgICAgIGZvbGRfcGxvdCA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAjIGZpZ3VyZSBzZXR0aW5ncwogICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduID0gJ2NlbnRlcicsCiAgICAgICAgICAgICAgICAgICAgICBmaWcud2lkdGggPSAyMCwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5oZWlnaHQgPSAxNSwKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgIyBzb21ldGhpbmcgYWJvdXQgc2VlZCwgY2h1bmsgYW5kIFJtYXJrZG93biBjb21waWxhdGlvbgogICAgICAgICAgICAgICAgICAgICAgIyBodHRwczovL3N0YWNrb3ZlcmZsb3cuY29tL3F1ZXN0aW9ucy8zOTQxNzAwMy9sb25nLXZlY3RvcnMtbm90LXN1cHBvcnRlZC15ZXQtZXJyb3ItaW4tcm1kLWJ1dC1ub3QtaW4tci1zY3JpcHQKICAgICAgICAgICAgICAgICAgICAgICMgY2FjaGUgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgY2FjaGUubGF6eSA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgIyBhZGQgcnVudGltZSBhZnRlciBjaHVuawogICAgICAgICAgICAgICAgICAgICAgdGltZV9pdCA9IEZBTFNFKQpgYGAKCgpUaGlzIGZpbGUgaXMgdXNlZCB0byBhbmFseXNlIHRoZSBJRkUgZ3JhbnVsYXIgYW5kIHNwaW5vdXMgZGF0YXNldC4KCmBgYHtyIGxpYnJhcnl9CmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCgoubGliUGF0aHMoKQpgYGAKCiMgUHJlcGFyYXRpb24KCkluIHRoaXMgc2VjdGlvbiwgd2Ugc2V0IHRoZSBnbG9iYWwgc2V0dGluZ3Mgb2YgdGhlIGFuYWx5c2lzLiBXZSB3aWxsIHN0b3JlIGRhdGEgdGhlcmUgOgoKYGBge3Igb3V0X2Rpcn0Kc2F2ZV9uYW1lID0gImlmZWdzIgpvdXRfZGlyID0gIi4iCmBgYAoKV2UgbG9hZCB0aGUgZGF0YXNldCA6CgpgYGB7ciBsb2FkX3NvYmp9CnNvYmogPSByZWFkUkRTKHBhc3RlMChvdXRfZGlyLCAiLyIsIHNhdmVfbmFtZSwgIl9zb2JqLnJkcyIpKQpzb2JqCmBgYAoKV2UgbG9hZCB0aGUgc2FtcGxlIGluZm9ybWF0aW9uIDoKCmBgYHtyIGN1c3RvbV9wYWxldHRlX3NhbXBsZSwgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDZ9CnNhbXBsZV9pbmZvID0gcmVhZFJEUyhwYXN0ZTAob3V0X2RpciwgIi8uLi8uLi8xX21ldGFkYXRhL2hzX2hkX3NhbXBsZV9pbmZvLnJkcyIpKQpwcm9qZWN0X25hbWVzX29pID0gc2FtcGxlX2luZm8kcHJvamVjdF9uYW1lCgpncmFwaGljczo6cGllKHJlcCgxLCBucm93KHNhbXBsZV9pbmZvKSksCiAgICAgICAgICAgICAgY29sID0gc2FtcGxlX2luZm8kY29sb3IsCiAgICAgICAgICAgICAgbGFiZWxzID0gc2FtcGxlX2luZm8kcHJvamVjdF9uYW1lKQpgYGAKCkhlcmUgYXJlIGN1c3RvbSBjb2xvcnMgZm9yIGVhY2ggY2VsbCB0eXBlIDoKCmBgYHtyIGNvbG9yX21hcmtlcnMsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMS4yLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KY29sb3JfbWFya2VycyA9IHJlYWRSRFMocGFzdGUwKG91dF9kaXIsICIvLi4vLi4vMV9tZXRhZGF0YS9oc19oZF9jb2xvcl9tYXJrZXJzLnJkcyIpKQoKZGF0YS5mcmFtZShjZWxsX3R5cGUgPSBuYW1lcyhjb2xvcl9tYXJrZXJzKSwKICAgICAgICAgICBjb2xvciA9IHVubGlzdChjb2xvcl9tYXJrZXJzKSkgJT4lCiAgZ2dwbG90Mjo6Z2dwbG90KC4sIGFlcyh4ID0gY2VsbF90eXBlLCB5ID0gMCwgZmlsbCA9IGNlbGxfdHlwZSkpICsKICBnZ3Bsb3QyOjpnZW9tX3BvaW50KHBjaCA9IDIxLCBzaXplID0gNSkgKwogIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHVubGlzdChjb2xvcl9tYXJrZXJzKSwgYnJlYWtzID0gbmFtZXMoY29sb3JfbWFya2VycykpICsKICBnZ3Bsb3QyOjp0aGVtZV9jbGFzc2ljKCkgKwogIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgICAgICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMzAsIGhqdXN0ID0gMSkpCmBgYAoKVGhpcyBpcyB0aGUgcHJvamVjdGlvbiBvZiBpbnRlcmVzdCA6CgpgYGB7ciBuYW1lMkR9Cm5hbWUyRCA9ICJoYXJtb255XzIyX3RzbmUiCmBgYAoKV2UgZGVzaWduIGEgY3VzdG9tIGZ1bmN0aW9uIHRvIG1ha2UgdGhlIEdTRUEgcGxvdCBhbmQgYSB3b3JkIGNsb3VkIGdyYXBoIDoKCmBgYHtyIG1ha2VfZ3NlYV9wbG90LCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KbWFrZV9nc2VhX3Bsb3QgPSBmdW5jdGlvbihnc2VhX3Jlc3VsdHMsIGdzX29pLCBmb2xkX2NoYW5nZSwgbWV0cmljID0gIkZDIikgewogIGZvbGRfY2hhbmdlJG1ldHJpYyA9IGZvbGRfY2hhbmdlWywgbWV0cmljXQogIAogIHBsb3RfbGlzdCA9IGxhcHBseShnc19vaSwgRlVOID0gZnVuY3Rpb24oZ2VuZV9zZXQpIHsKICAgICMgR2VuZSBzZXQgY29udGVudAogICAgZ3NfY29udGVudCA9IGdlbmVfc2V0cyAlPiUKICAgICAgZHBseXI6OmZpbHRlcihnc19uYW1lID09IGdlbmVfc2V0KSAlPiUKICAgICAgZHBseXI6OnB1bGwoZW5zZW1ibF9nZW5lKSAlPiUKICAgICAgdW5pcXVlKCkKICAgIAogICAgIyBHZW5lIHNldCBzaXplCiAgICBuYl9nZW5lcyA9IGxlbmd0aChnc19jb250ZW50KQogICAgCiAgICAjIEVucmljaG1lbnQgbWV0cmljcwogICAgTkVTID0gZ3NlYV9yZXN1bHRzQHJlc3VsdFtnZW5lX3NldCwgIk5FUyJdCiAgICBwLmFkanVzdCA9IGdzZWFfcmVzdWx0c0ByZXN1bHRbZ2VuZV9zZXQsICJwLmFkanVzdCJdICU+JQogICAgICByb3VuZCguLCA0KQogICAgcXZhbHVlcyA9IGdzZWFfcmVzdWx0c0ByZXN1bHRbZ2VuZV9zZXQsICJxdmFsdWVzIl0KICAgIAogICAgaWYgKHAuYWRqdXN0ID4gMC4wNSkgewogICAgICBwLmFkanVzdCA9IHBhc3RlMCgiPHNwYW4gc3R5bGU9J2NvbG9yOnJlZDsnPiIsIHAuYWRqdXN0LCAiPC9zcGFuPiIpCiAgICB9CiAgICAKICAgIG15X3N1YnRpdGxlID0gcGFzdGUwKCJcbk5FUyA6ICIsIHJvdW5kKE5FUywgMiksCiAgICAgICAgICAgICAgICAgICAgICAgICAiIHwgcGFkaiA6ICIsIHAuYWRqdXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgIiB8IHF2YWwgOiAiLCByb3VuZChxdmFsdWVzLCA0KSwKICAgICAgICAgICAgICAgICAgICAgICAgICIgfCBzZXQgc2l6ZSA6ICIsIG5iX2dlbmVzLCAiIGdlbmVzIikKICAgIAogICAgIyBTaXplIGxpbWl0cwogICAgbG93ZXJfRkMgPSBtaW4oZm9sZF9jaGFuZ2VbZ3NfY29udGVudCwgXSRtZXRyaWMsIG5hLnJtID0gVFJVRSkKICAgIHVwcGVyX0ZDID0gbWF4KGZvbGRfY2hhbmdlW2dzX2NvbnRlbnQsIF0kbWV0cmljLCBuYS5ybSA9IFRSVUUpCiAgICAKICAgICMgUGxvdAogICAgcCA9IGVucmljaHBsb3Q6OmdzZWFwbG90Mih4ID0gZ3NlYV9yZXN1bHRzLCBnZW5lU2V0SUQgPSBnZW5lX3NldCkgKwogICAgICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gZ2VuZV9zZXQsCiAgICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSBteV9zdWJ0aXRsZSkgKwogICAgICBnZ3Bsb3QyOjp0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCBmYWNlID0gImJvbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1hcmdpbiA9IGdncGxvdDI6Om1hcmdpbigzLCAzLCA1LCAzKSksCiAgICAgICAgICAgICAgICAgICAgIHBsb3Quc3VidGl0bGUgPSBnZ3RleHQ6OmVsZW1lbnRfbWFya2Rvd24oaGp1c3QgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEwKSkKICAgIAogICAgd2MgPSBnZ3Bsb3QyOjpnZ3Bsb3QoZm9sZF9jaGFuZ2VbZ3NfY29udGVudCwgXSwKICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhsYWJlbCA9IGdlbmVfbmFtZSwgc2l6ZSA9IGFicyhtZXRyaWMpLCBjb2xvciA9IG1ldHJpYykpICsKICAgICAgZ2d3b3JkY2xvdWQ6Omdlb21fdGV4dF93b3JkY2xvdWRfYXJlYShzaG93LmxlZ2VuZCA9IFRSVUUpICsKICAgICAgZ2dwbG90Mjo6c2NhbGVfY29sb3JfZ3JhZGllbnQyKAogICAgICAgIG5hbWUgPSBtZXRyaWMsCiAgICAgICAgbG93ID0gYXF1YXJpdXM6OmNvbG9yX2NudlsxXSwKICAgICAgICBtaWQgPSAiZ3JheTcwIiwgbWlkcG9pbnQgPSAwLAogICAgICAgIGhpZ2ggPSBhcXVhcml1czo6Y29sb3JfY252WzNdKSArCiAgICAgIGdncGxvdDI6OnNjYWxlX3NpemVfYXJlYShtYXhfc2l6ZSA9IDcpICsKICAgICAgZ2dwbG90Mjo6dGhlbWVfbWluaW1hbCgpICsKICAgICAgZ2dwbG90Mjo6Z3VpZGVzKHNpemUgPSAibm9uZSIpCiAgICAKICAgIHJldHVybihsaXN0KHAsIHdjKSkKICB9KSAlPiUgdW5saXN0KC4sIHJlY3Vyc2l2ZSA9IEZBTFNFKQogIAogIHJldHVybihwbG90X2xpc3QpCn0KYGBgCgojIFZpc3VhbGl6YXRpb24KCiMjIEdlbmUgZXhwcmVzc2lvbgoKV2UgdmlzdWFsaXplIGdlbmUgZXhwcmVzc2lvbiBmb3Igc29tZSBtYXJrZXJzIDoKCmBgYHtyIHBsb3RfbGlzdF9mZWF0dXJlcywgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA3fQpmZWF0dXJlcyA9IGMoInBlcmNlbnQubXQiLCAibkZlYXR1cmVfUk5BIiwgIlRLMSIsCiAgICAgICAgICAgICAiU1BJTks1IiwgIktSVDEiLCAiS1JUREFQIikKCnBsb3RfbGlzdCA9IGxhcHBseShmZWF0dXJlcywgRlVOID0gZnVuY3Rpb24ob25lX2dlbmUpIHsKICBTZXVyYXQ6OkZlYXR1cmVQbG90KHNvYmosIGZlYXR1cmVzID0gb25lX2dlbmUsCiAgICAgICAgICAgICAgICAgICAgICByZWR1Y3Rpb24gPSBuYW1lMkQpICsKICAgIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpICsKICAgIGdncGxvdDI6OnNjYWxlX2NvbG9yX2dyYWRpZW50bihjb2xvcnMgPSBhcXVhcml1czo6Y29sb3JfZ2VuZSkgKwogICAgU2V1cmF0OjpOb0F4ZXMoKQp9KQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbmNvbCA9IDMpCmBgYAoKIyMgQ2x1c3RlcnMKCldlIHZpc3VhbGl6ZSBjbHVzdGVycyA6CgpgYGB7ciBzZWVfY2x1c3RlcmluZywgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDR9CmNsdXN0ZXJfcGxvdCA9IFNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkQsIGxhYmVsID0gVFJVRSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCmNsdXN0ZXJfcGxvdApgYGAKCiMjIENsdXN0ZXIgYW5ub3RhdGlvbgoKS1JUMSBhbmQgS1JUREFQIGJlaW5nIHNwZWNpZmljYWxseSBleHByZXNzZWQgaW4gSUZFIHNwaW5vdXMsIHdlIG1ha2UgYSBjbHVzdGVyIGFubm90YXRpb246CgpgYGB7ciBjbHVzdGVyX3R5cGV9CnNvYmokY2x1c3Rlcl90eXBlID0gY2FzZV93aGVuKAogIHNvYmokc2V1cmF0X2NsdXN0ZXJzICVpbiUgYyg1LDYpIH4gIklGRSBwcm9saWZlcmF0aXZlIiwKICBzb2JqJHNldXJhdF9jbHVzdGVycyAlaW4lIGMoMSwyLDMpIH4gIklGRSBzcGlub3VzIiwKICBzb2JqJHNldXJhdF9jbHVzdGVycyAlaW4lIGMoMCw0KSB+ICJJRkUgZ3JhbnVsYXIiLAogIFRSVUUgfiAib3RoZXJzIikgJT4lICMgb3RoZXJzOiA4LDksNywwCiAgZmFjdG9yKC4sIGxldmVscyA9IGMoIklGRSBzcGlub3VzIiwgIklGRSBncmFudWxhciIsCiAgICAgICAgICAgICAgICAgICAgICAgIklGRSBwcm9saWZlcmF0aXZlIiwgIm90aGVycyIpKQoKY2x1c3Rlcl9jb2xvciA9IHNldE5hbWVzKG5tID0gYygiSUZFIHNwaW5vdXMiLCAiSUZFIGdyYW51bGFyIiwgIklGRSBwcm9saWZlcmF0aXZlIiwgIm90aGVycyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgYygic3RlZWxibHVlNCIsICJsaWdodHNreWJsdWUiLCAiZ3JheTUwIiwgImdyYXkxMCIpKQpgYGAKCldlIHZpc3VhbGl6ZSB0aGUgY2x1c3RlciBhbm5vdGF0aW9uIHNwbGl0IGJ5IHNhbXBsZSA6CgpgYGB7ciBwbG90X3NwbGl0X2RpbXJlZCwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA3fQpwbG90X2xpc3QgPSBhcXVhcml1czo6cGxvdF9zcGxpdF9kaW1yZWQoc29iaiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9IG5hbWUyRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNwbGl0X2J5ID0gInByb2plY3RfbmFtZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cF9ieSA9ICJjbHVzdGVyX3R5cGUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3BsaXRfY29sb3IgPSBzZXROYW1lcyhzYW1wbGVfaW5mbyRjb2xvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm0gPSBzYW1wbGVfaW5mbyRwcm9qZWN0X25hbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfY29sb3IgPSBjbHVzdGVyX2NvbG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmdfcHRfc2l6ZSA9IDAuNSwgbWFpbl9wdF9zaXplID0gMC41KQoKcGxvdF9saXN0W1tsZW5ndGgocGxvdF9saXN0KSArIDFdXSA9IFNldXJhdDo6RGltUGxvdChzb2JqLCByZWR1Y3Rpb24gPSBuYW1lMkQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAiY2x1c3Rlcl90eXBlIikgKwogIGdncGxvdDI6OnNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjbHVzdGVyX2NvbG9yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBuYW1lcyhjbHVzdGVyX2NvbG9yKSkgKwogIFNldXJhdDo6Tm9BeGVzKCkgKwogIGdncGxvdDI6OnRoZW1lKGFzcGVjdC5yYXRpbyA9IDEpCgpwYXRjaHdvcms6OndyYXBfcGxvdHMocGxvdF9saXN0LCBuY29sID0gNCkgJgogIFNldXJhdDo6Tm9MZWdlbmQoKQpgYGAKCiMgRGlmZmVyZW50aWFsIGV4cHJlc3Npb24KCldlIHNhdmUgdGhlIHJlc3VsdHMgaW4gYSBsaXN0IDoKCmBgYHtyIGxpc3RfcmVzdWx0c30KbGlzdF9yZXN1bHRzID0gbGlzdCgpCmBgYAoKV2UgbWFrZSBvdmVyLXJlcHJlc2VudGF0aW9uIGFuYWx5c2lzIGZvciBlYWNoIGdyb3VwIG9mIGdlbmVzLiBXZSBsb2FkIGdlbmUgc2V0cyBmcm9tIE1TaWdEQiA6CgpgYGB7ciBnZW5lX3NldHN9CmdlbmVfc2V0cyA9IGFxdWFyaXVzOjpnZXRfZ2VuZV9zZXRzKHNwZWNpZXMgPSAiSG9tbyBzYXBpZW5zIikKZ2VuZV9zZXRzID0gZ2VuZV9zZXRzJGdlbmVfc2V0cwoKaGVhZChnZW5lX3NldHMpCmBgYAoKSG93IG1hbnkgZ2VuZSBzZXRzID8KCmBgYHtyIGNvdW50X2dlbmVfc2V0c30KZ2VuZV9zZXRzWywgYygiZ3Nfc3ViY2F0IiwgImdzX25hbWUiKV0gJT4lCiAgZHBseXI6OmRpc3RpbmN0KCkgJT4lCiAgZHBseXI6OnB1bGwoZ3Nfc3ViY2F0KSAlPiUKICB0YWJsZSgpICU+JQogIGFzLmRhdGEuZnJhbWUudGFibGUoKSAlPiUKICBgY29sbmFtZXM8LWAoYygiQ2F0ZWdvcnkiLCAiTmIgZ2VuZSBzZXRzIikpCmBgYAoKV2UgZ2V0IGdlbmUgbmFtZSBhbmQgZ2VuZSBJRCBjb3JyZXNwb25kZW5jZSA6CgpgYGB7ciBnZW5lX2NvcnJlc3B9CmdlbmVfY29ycmVzcCA9IHNvYmpAYXNzYXlzW1siUk5BIl1dQG1ldGEuZmVhdHVyZXNbLCBjKCJnZW5lX25hbWUiLCAiRW5zZW1ibF9JRCIpXSAlPiUKICBgY29sbmFtZXM8LWAoYygiTkFNRSIsICJJRCIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKElEID0gYXMuY2hhcmFjdGVyKElEKSkKcm93bmFtZXMoZ2VuZV9jb3JyZXNwKSA9IGdlbmVfY29ycmVzcCRJRAoKaGVhZChnZW5lX2NvcnJlc3ApCmBgYAoKIyMgR3JhbnVsYXIgdnMgc3Bpbm91cwoKV2UgcGVyZm9ybSBhIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIGJldHdlZW4gZ3JhbnVsYXIgYW5kIHNwaW5vdXMgY2x1c3RlcnMuCgpgYGB7ciBzZXRfaWRlbnRzMX0KZ3JvdXBfbmFtZSA9ICJncmFudWxhcl92c19zcGlub3VzIgoKU2V1cmF0OjpJZGVudHMoc29iaikgPSBzb2JqJGNsdXN0ZXJfdHlwZQp0YWJsZShTZXVyYXQ6OklkZW50cyhzb2JqKSkKYGBgCgpXZSB2aXN1YWxpemUgdGhlc2UgcG9wdWxhdGlvbnMgOgoKYGBge3Igc2VlX3JlZF9ibHVlLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gN30KYXF1YXJpdXM6OnBsb3RfcmVkX2FuZF9ibHVlKHNvYmosIHJlZHVjdGlvbiA9IG5hbWUyRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMSA9ICJJRkUgZ3JhbnVsYXIiLCBncm91cDIgPSAiSUZFIHNwaW5vdXMiKQpgYGAKCiMjIyBERQoKV2UgaWRlbnRpZnkgREUgZ2VuZXMgYmV0d2VlbiBJRkUgZ3JhbnVsYXIgYW5kIElGRSBzcGlub3VzIDoKCmBgYHtyIGRlX2lmZWdzLCBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0ID0gNX0KbWFyayA9IFNldXJhdDo6RmluZE1hcmtlcnMoc29iaiwgaWRlbnQuMSA9ICJJRkUgZ3JhbnVsYXIiLCBpZGVudC4yID0gIklGRSBzcGlub3VzIikKCm1hcmsgPSBtYXJrICU+JQogIGRwbHlyOjpmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoLWF2Z19sb2dGQywgcGN0LjEgLSBwY3QuMikKCmxpc3RfcmVzdWx0c1tbZ3JvdXBfbmFtZV1dJG1hcmsgPSBtYXJrCgpkaW0obWFyaykKaGVhZChtYXJrLCBuID0gMjApCmBgYAoKCiMjIyBVcCBpbiBJRkUgZ3JhbnVsYXIKCldlIGV4cGxvcmUgZW5yaWNobWVudCBpbiBnZW5lIHNldHMgZm9yIEhTIHBvcHVsYXRpb24uCgpgYGB7ciBvcmFfaWZlZ3NfaHMsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNn0KZ2VuZXNfb2ZfaW50ZXJlc3QgPSBtYXJrICU+JQogIGRwbHlyOjpmaWx0ZXIoYXZnX2xvZ0ZDID4gMCkgJT4lCiAgcm93bmFtZXMoKQoKZW5yaWNocl9yZXN1bHRzID0gYXF1YXJpdXM6OnJ1bl9lbnJpY2hyKGdlbmVfbmFtZXMgPSBnZW5lc19vZl9pbnRlcmVzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfY29ycmVzcCA9IGdlbmVfY29ycmVzcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfc2V0cyA9IGdlbmVfc2V0c1ssIGMoImdzX25hbWUiLCAiZW5zZW1ibF9nZW5lIildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFrZV9wbG90ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfdGl0bGUgPSAiVXAtcmVndWxhdGVkIGluIElGRSBncmFudWxhciBjb21wYXJlZCB0byBJRkUgc3Bpbm91cyIpCgpsaXN0X3Jlc3VsdHNbW2dyb3VwX25hbWVdXSRlbnJpY2hyX2hzID0gZW5yaWNocl9yZXN1bHRzJGVnbwoKZW5yaWNocl9yZXN1bHRzJHBsb3QgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkKYGBgCgojIyMgVXAgaW4gSUZFIHNwaW5vdXMKCldlIGV4cGxvcmUgZW5yaWNobWVudCBpbiBnZW5lIHNldHMgZm9yIEhEIHBvcHVsYXRpb24uCgpgYGB7ciBvcmFfaWZlZ3NfaGQsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNn0KZ2VuZXNfb2ZfaW50ZXJlc3QgPSBtYXJrICU+JQogIGRwbHlyOjpmaWx0ZXIoYXZnX2xvZ0ZDIDwgMCkgJT4lCiAgcm93bmFtZXMoKQoKZW5yaWNocl9yZXN1bHRzID0gYXF1YXJpdXM6OnJ1bl9lbnJpY2hyKGdlbmVfbmFtZXMgPSBnZW5lc19vZl9pbnRlcmVzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfY29ycmVzcCA9IGdlbmVfY29ycmVzcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfc2V0cyA9IGdlbmVfc2V0c1ssIGMoImdzX25hbWUiLCAiZW5zZW1ibF9nZW5lIildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFrZV9wbG90ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfdGl0bGUgPSAiRG93bi1yZWd1bGF0ZWQgaW4gSUZFIGdyYW51bGFyIGNvbXBhcmVkIHRvIElGRSBzcGlub3VzIikKCmxpc3RfcmVzdWx0c1tbZ3JvdXBfbmFtZV1dJGVucmljaHJfaGQgPSBlbnJpY2hyX3Jlc3VsdHMkZWdvCgplbnJpY2hyX3Jlc3VsdHMkcGxvdCArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKQpgYGAKCgojIyMgR1NFQQoKV2UgcnVuIGEgR1NFQSBmb3IgYWxsIGdlbmUgc2V0cywgZnJvbSB0aGUgZnVsbCBjb3VudCBtYXRyaXggOgoKYGBge3IgZ3NlYV9pbl9pZmVncywgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA0MH0KcmFua2VkX2dlbmVfbGlzdCA9IGFxdWFyaXVzOjpydW5fZm9sZGNoYW5nZShTZXVyYXQ6OkdldEFzc2F5RGF0YShzb2JqLCBhc3NheSA9ICJSTkEiLCBzbG90ID0gImNvdW50cyIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMSA9IGNvbG5hbWVzKHNvYmopW3NvYmpAYWN0aXZlLmlkZW50ICVpbiUgIklGRSBncmFudWxhciJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMiA9IGNvbG5hbWVzKHNvYmopW3NvYmpAYWN0aXZlLmlkZW50ICVpbiUgIklGRSBzcGlub3VzIl0pCm5hbWVzKHJhbmtlZF9nZW5lX2xpc3QpID0gZ2VuZV9jb3JyZXNwJElECgpnc2VhX3Jlc3VsdHMgPSBhcXVhcml1czo6Z3NlYV9ydW4ocmFua2VkX2dlbmVfbGlzdCA9IHJhbmtlZF9nZW5lX2xpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX3NldHMgPSBnZW5lX3NldHNbLCBjKCJnc19uYW1lIiwgImVuc2VtYmxfZ2VuZSIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdTRUFfcF92YWxfdGhyZXNoID0gMSkKCmxpc3RfcmVzdWx0c1tbZ3JvdXBfbmFtZV1dJGdzZWEgPSBnc2VhX3Jlc3VsdHMKCmdzZWFfcmVzdWx0c0ByZXN1bHQgJT4lCiAgZHBseXI6OmZpbHRlcihwdmFsdWUgPCAwLjA1KSAlPiUKICBkcGx5cjo6dG9wX24oLiwgbiA9IDIwMCwgd3QgPSBhYnMoTkVTKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b29fbG9uZyA9IGlmZWxzZShuY2hhcihJRCkgPiA2MCwgeWVzID0gVFJVRSwgbm8gPSBGQUxTRSkpICU+JQogIGRwbHlyOjptdXRhdGUoSUQgPSBzdHJpbmdyOjpzdHJfc3ViKElELCBlbmQgPSA2MCkpICU+JQogIGRwbHlyOjptdXRhdGUoSUQgPSBpZmVsc2UodG9vX2xvbmcsIHllcyA9IHBhc3RlMChJRCwgIi4uLiIpLCBubyA9IElEKSkgJT4lCiAgYXF1YXJpdXM6OmdzZWFfcGxvdChzaG93X2xlZ2VuZCA9IFRSVUUpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gIkdTRUEgdXNpbmcgYWxsIGdlbmVzIChjb3VudCBtYXRyaXgpIikgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkKYGBgCgpXZSBjb21wdXRlIGEgZm9sZCBjaGFuZ2UgdGFibGUgdG8gbWFrZSBhIHdvcmRjbG91ZCA6CgpgYGB7ciBmb2xkX2NoYW5nZV9pZmVnc30KZm9sZF9jaGFuZ2UgPSBnZW5lX2NvcnJlc3AKY29sbmFtZXMoZm9sZF9jaGFuZ2UpID0gYygiZ2VuZV9uYW1lIiwgIklEIikKcm93bmFtZXMoZm9sZF9jaGFuZ2UpID0gZm9sZF9jaGFuZ2UkSUQKZm9sZF9jaGFuZ2UkRkMgPSByYW5rZWRfZ2VuZV9saXN0W3Jvd25hbWVzKGZvbGRfY2hhbmdlKV0KZm9sZF9jaGFuZ2UkcGN0LjEgPSBTZXVyYXQ6OkdldEFzc2F5RGF0YShzb2JqLCBhc3NheSA9ICJSTkEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNsb3QgPSAiY291bnRzIilbLCBzb2JqQGFjdGl2ZS5pZGVudCA9PSAiSUZFIGdyYW51bGFyIl0gJT4lCiAgYXBwbHkoLiwgTUFSR0lOID0gMSwgRlVOID0gZnVuY3Rpb24ob25lX3JvdykgewogICAgcmV0dXJuKCBtZWFuKG9uZV9yb3cgIT0gMCkgKQogIH0pCmZvbGRfY2hhbmdlJHBjdC4yID0gU2V1cmF0OjpHZXRBc3NheURhdGEoc29iaiwgYXNzYXkgPSAiUk5BIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzbG90ID0gImNvdW50cyIpWywgc29iakBhY3RpdmUuaWRlbnQgPT0gIklGRSBzcGlub3VzIl0gJT4lCiAgYXBwbHkoLiwgTUFSR0lOID0gMSwgRlVOID0gZnVuY3Rpb24ob25lX3JvdykgewogICAgcmV0dXJuKCBtZWFuKG9uZV9yb3cgIT0gMCkgKQogIH0pCmZvbGRfY2hhbmdlJEZDX3hfcGN0ID0gaWZlbHNlKGZvbGRfY2hhbmdlJEZDID4gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeWVzID0gZm9sZF9jaGFuZ2UkRkMgKiBmb2xkX2NoYW5nZSRwY3QuMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm8gPSBmb2xkX2NoYW5nZSRGQyAqIGZvbGRfY2hhbmdlJHBjdC4yKQoKZGltKGZvbGRfY2hhbmdlKSA7IGhlYWQoZm9sZF9jaGFuZ2UpCmBgYAoKV2UgbWFrZSB0aGUgZ3NlYSBwbG90IGZvciBzb21lIGdlbmUgc2V0cyA6CgpgYGB7ciBnc2VhX3Bsb3Rfd2l0aGluX2lmZWdzLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDI1fQpnc19vaSA9IGMoIkdPQlBfS0VSQVRJTklaQVRJT04iLAogICAgICAgICAgIkdPQ0NfQ09STklGSUVEX0VOVkVMT1BFIiwKICAgICAgICAgICJHT0JQX0NFTExVTEFSX1JFU1BPTlNFX1RPX0NPUFBFUl9JT04iLAogICAgICAgICAgIkdPQlBfQ0VMTFVMQVJfUkVTUE9OU0VfVE9fQ0FETUlVTV9JT04iLAogICAgICAgICAgIlJFQUNUT01FX1JFU1BPTlNFX1RPX01FVEFMX0lPTlMiLAogICAgICAgICAgIldQX1RHRkJFVEFfUkVDRVBUT1JfU0lHTkFMSU5HIikKCnBsb3RfbGlzdCA9IG1ha2VfZ3NlYV9wbG90KGdzZWFfcmVzdWx0cywgZ3Nfb2ksIGZvbGRfY2hhbmdlLCAiRkNfeF9wY3QiKQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbmNvbCA9IDIsIHdpZHRocyA9IGMoMiwxLjUpKQpgYGAKCldlIGF0dHJpYnV0ZSBzY29yZSBmb3IgZWFjaCBnZW5lIHNldHMgdG8gYWxsIGNlbGxzLCB0byB2aXN1YWxpemUgdGhlIHNjb3JlcyBhcyB2aW9saW4gcGxvdHMuCgpgYGB7ciBzY29yZV9nZW5lX3NldHN9CmZvciAob25lX2dlbmVfc2V0IGluIGdzX29pKSB7CiAgbmV3X2NvbHVtbiA9IHBhc3RlMCgic2NvcmVfIiwgb25lX2dlbmVfc2V0KQogIAogICMgZ2VuZSBzZXQgY29udGVudAogIGdzX2NvbnRlbnQgPSBnZW5lX3NldHMgJT4lCiAgICBkcGx5cjo6ZmlsdGVyKGdzX25hbWUgPT0gb25lX2dlbmVfc2V0KSAlPiUKICAgIGRwbHlyOjpwdWxsKGdlbmVfc3ltYm9sKSAlPiUKICAgIHVubGlzdCgpCiAgCiAgIyBhZGQgc2NvcmUKICBzb2JqQG1ldGEuZGF0YVssIG5ld19jb2x1bW5dID0gU2V1cmF0OjpBZGRNb2R1bGVTY29yZShzb2JqLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gbGlzdChnc19jb250ZW50KSkkQ2x1c3RlcjEKfQoKaGVhZChzb2JqQG1ldGEuZGF0YSkKYGBgCgpXZSB2aXN1YWxpemUgdGhlIHNjb3JlcyBhcyB2aW9saW4gcGxvdHMgOgoKYGBge3Igc2VlX3Njb3JlX2dzZWEsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTJ9CnBsb3RfbGlzdCA9IGxhcHBseShnc19vaSwgRlVOID0gZnVuY3Rpb24ob25lX2dzKSB7CiAgcCA9IFNldXJhdDo6VmxuUGxvdChzb2JqLCBmZWF0dXJlcyA9IHBhc3RlMCgic2NvcmVfIiwgb25lX2dzKSwKICAgICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNsdXN0ZXJfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICBzcGxpdC5ieSA9ICJzYW1wbGVfdHlwZSIpICsKICAgIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNDNTVGNDAiLCAiIzJDNzhFNiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygiSFMiLCAiSEQiKSkgKwogICAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IG9uZV9ncykgKwogICAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKQogIAogIHJldHVybihwKQp9KQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbmNvbCA9IDIpCmBgYAoKIyMgSFMgdnMgSEQKCldlIHBlcmZvcm0gYSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcyBmb3IgYWxsIGNsdXN0ZXJzLCBiZXR3ZWVuIEhTIHBhdGllbnRzIChIUykgYW5kIGhlYWx0aHkgZG9ub3JzIChIRCkuCgpgYGB7ciBzZXRfaWRlbnRzfQpncm91cF9uYW1lID0gIkhTX3ZzX0hEIgoKU2V1cmF0OjpJZGVudHMoc29iaikgPSBzb2JqJHNhbXBsZV90eXBlCnRhYmxlKFNldXJhdDo6SWRlbnRzKHNvYmopKQpgYGAKCiMjIyBERQoKV2UgaWRlbnRpZnkgREUgZ2VuZXMgYmV0d2VlbiBIUyBhbmQgSEQgOgoKYGBge3IgZGVfaWZlZ3NfaHNfdnNfaGQsIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQgPSA1fQptYXJrID0gU2V1cmF0OjpGaW5kTWFya2Vycyhzb2JqLCBpZGVudC4xID0gIkhTIiwgaWRlbnQuMiA9ICJIRCIpCgptYXJrID0gbWFyayAlPiUKICBkcGx5cjo6ZmlsdGVyKHBfdmFsX2FkaiA8IDAuMDUpICU+JQogIGRwbHlyOjphcnJhbmdlKC1hdmdfbG9nRkMsIHBjdC4xIC0gcGN0LjIpCgpsaXN0X3Jlc3VsdHNbW2dyb3VwX25hbWVdXSRtYXJrID0gbWFyawoKZGltKG1hcmspCmhlYWQobWFyaywgbiA9IDIwKQpgYGAKCgojIyMgVXAgaW4gSFMKCldlIGV4cGxvcmUgZW5yaWNobWVudCBpbiBnZW5lIHNldHMgZm9yIEhTIHBvcHVsYXRpb24uCgpgYGB7ciBvcmFfaWZlZ3NfaHNfaHNfdnNfaGQsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNn0KZ2VuZXNfb2ZfaW50ZXJlc3QgPSBtYXJrICU+JQogIGRwbHlyOjpmaWx0ZXIoYXZnX2xvZ0ZDID4gMCkgJT4lCiAgcm93bmFtZXMoKQoKZW5yaWNocl9yZXN1bHRzID0gYXF1YXJpdXM6OnJ1bl9lbnJpY2hyKGdlbmVfbmFtZXMgPSBnZW5lc19vZl9pbnRlcmVzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfY29ycmVzcCA9IGdlbmVfY29ycmVzcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmVfc2V0cyA9IGdlbmVfc2V0c1ssIGMoImdzX25hbWUiLCAiZW5zZW1ibF9nZW5lIildLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFrZV9wbG90ID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RfdGl0bGUgPSAiVXAtcmVndWxhdGVkIGluIEhTIGNvbXBhcmVkIHRvIEhEIikKCmxpc3RfcmVzdWx0c1tbZ3JvdXBfbmFtZV1dJGVucmljaHJfaHMgPSBlbnJpY2hyX3Jlc3VsdHMkZWdvCgplbnJpY2hyX3Jlc3VsdHMkcGxvdCArCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgpKQpgYGAKCiMjIyBVcCBpbiBIRAoKV2UgZXhwbG9yZSBlbnJpY2htZW50IGluIGdlbmUgc2V0cyBmb3IgSEQgcG9wdWxhdGlvbi4KCmBgYHtyIG9yYV9pZmVnc19oZF9oc192c19oZCwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA2fQpnZW5lc19vZl9pbnRlcmVzdCA9IG1hcmsgJT4lCiAgZHBseXI6OmZpbHRlcihhdmdfbG9nRkMgPCAwKSAlPiUKICByb3duYW1lcygpCgplbnJpY2hyX3Jlc3VsdHMgPSBhcXVhcml1czo6cnVuX2VucmljaHIoZ2VuZV9uYW1lcyA9IGdlbmVzX29mX2ludGVyZXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9jb3JyZXNwID0gZ2VuZV9jb3JyZXNwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZV9zZXRzID0gZ2VuZV9zZXRzWywgYygiZ3NfbmFtZSIsICJlbnNlbWJsX2dlbmUiKV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYWtlX3Bsb3QgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdF90aXRsZSA9ICJEb3duLXJlZ3VsYXRlZCBpbiBIUyBjb21wYXJlZCB0byBIRCIpCgpsaXN0X3Jlc3VsdHNbW2dyb3VwX25hbWVdXSRlbnJpY2hyX2hkID0gZW5yaWNocl9yZXN1bHRzJGVnbwoKZW5yaWNocl9yZXN1bHRzJHBsb3QgKwogIGdncGxvdDI6OnRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkKYGBgCgoKIyMjIEdTRUEKCldlIHJ1biBhIEdTRUEgZm9yIGFsbCBnZW5lIHNldHMsIGZyb20gdGhlIGZ1bGwgY291bnQgbWF0cml4IDoKCmBgYHtyIGdzZWFfaW5faWZlZ3NfaHNfdnNfaGQsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNDB9CnJhbmtlZF9nZW5lX2xpc3QgPSBhcXVhcml1czo6cnVuX2ZvbGRjaGFuZ2UoU2V1cmF0OjpHZXRBc3NheURhdGEoc29iaiwgYXNzYXkgPSAiUk5BIiwgc2xvdCA9ICJjb3VudHMiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cDEgPSBjb2xuYW1lcyhzb2JqKVtzb2JqQGFjdGl2ZS5pZGVudCAlaW4lICJIUyJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyb3VwMiA9IGNvbG5hbWVzKHNvYmopW3NvYmpAYWN0aXZlLmlkZW50ICVpbiUgIkhEIl0pCm5hbWVzKHJhbmtlZF9nZW5lX2xpc3QpID0gZ2VuZV9jb3JyZXNwJElECgpnc2VhX3Jlc3VsdHMgPSBhcXVhcml1czo6Z3NlYV9ydW4ocmFua2VkX2dlbmVfbGlzdCA9IHJhbmtlZF9nZW5lX2xpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lX3NldHMgPSBnZW5lX3NldHNbLCBjKCJnc19uYW1lIiwgImVuc2VtYmxfZ2VuZSIpXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdTRUFfcF92YWxfdGhyZXNoID0gMSkKCmxpc3RfcmVzdWx0c1tbZ3JvdXBfbmFtZV1dJGdzZWEgPSBnc2VhX3Jlc3VsdHMKCmdzZWFfcmVzdWx0c0ByZXN1bHQgJT4lCiAgZHBseXI6OmZpbHRlcihwdmFsdWUgPCAwLjA1KSAlPiUKICBkcGx5cjo6dG9wX24oLiwgbiA9IDIwMCwgd3QgPSBhYnMoTkVTKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b29fbG9uZyA9IGlmZWxzZShuY2hhcihJRCkgPiA2MCwgeWVzID0gVFJVRSwgbm8gPSBGQUxTRSkpICU+JQogIGRwbHlyOjptdXRhdGUoSUQgPSBzdHJpbmdyOjpzdHJfc3ViKElELCBlbmQgPSA2MCkpICU+JQogIGRwbHlyOjptdXRhdGUoSUQgPSBpZmVsc2UodG9vX2xvbmcsIHllcyA9IHBhc3RlMChJRCwgIi4uLiIpLCBubyA9IElEKSkgJT4lCiAgYXF1YXJpdXM6OmdzZWFfcGxvdChzaG93X2xlZ2VuZCA9IFRSVUUpICsKICBnZ3Bsb3QyOjpsYWJzKHRpdGxlID0gIkdTRUEgdXNpbmcgYWxsIGdlbmVzIChjb3VudCBtYXRyaXgpIikgKwogIGdncGxvdDI6OnRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSkKYGBgCgpXZSBjb21wdXRlIGEgZm9sZCBjaGFuZ2UgdGFibGUgdG8gbWFrZSBhIHdvcmRjbG91ZCA6CgpgYGB7ciBmb2xkX2NoYW5nZV9pZmVnc19oc192c19oZH0KZm9sZF9jaGFuZ2UgPSBnZW5lX2NvcnJlc3AKY29sbmFtZXMoZm9sZF9jaGFuZ2UpID0gYygiZ2VuZV9uYW1lIiwgIklEIikKcm93bmFtZXMoZm9sZF9jaGFuZ2UpID0gZm9sZF9jaGFuZ2UkSUQKZm9sZF9jaGFuZ2UkRkMgPSByYW5rZWRfZ2VuZV9saXN0W3Jvd25hbWVzKGZvbGRfY2hhbmdlKV0KZm9sZF9jaGFuZ2UkcGN0LjEgPSBTZXVyYXQ6OkdldEFzc2F5RGF0YShzb2JqLCBhc3NheSA9ICJSTkEiLCBzbG90ID0gImNvdW50cyIpWywgc29iakBhY3RpdmUuaWRlbnQgPT0gIkhTIl0gJT4lCiAgYXBwbHkoLiwgTUFSR0lOID0gMSwgRlVOID0gZnVuY3Rpb24ob25lX3JvdykgewogICAgcmV0dXJuKCBtZWFuKG9uZV9yb3cgIT0gMCkgKQogIH0pCmZvbGRfY2hhbmdlJHBjdC4yID0gU2V1cmF0OjpHZXRBc3NheURhdGEoc29iaiwgYXNzYXkgPSAiUk5BIiwgc2xvdCA9ICJjb3VudHMiKVssIHNvYmpAYWN0aXZlLmlkZW50ID09ICJIRCJdICU+JQogIGFwcGx5KC4sIE1BUkdJTiA9IDEsIEZVTiA9IGZ1bmN0aW9uKG9uZV9yb3cpIHsKICAgIHJldHVybiggbWVhbihvbmVfcm93ICE9IDApICkKICB9KQpmb2xkX2NoYW5nZSRGQ194X3BjdCA9IGlmZWxzZShmb2xkX2NoYW5nZSRGQyA+IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9IGZvbGRfY2hhbmdlJEZDICogZm9sZF9jaGFuZ2UkcGN0LjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vID0gZm9sZF9jaGFuZ2UkRkMgKiBmb2xkX2NoYW5nZSRwY3QuMikKCmRpbShmb2xkX2NoYW5nZSkgOyBoZWFkKGZvbGRfY2hhbmdlKQpgYGAKCldlIG1ha2UgdGhlIGdzZWEgcGxvdCBmb3Igc29tZSBnZW5lIHNldHMgOgoKYGBge3IgZ3NlYV9wbG90X3dpdGhpbl9pZmVnc19oc192c19oZCwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAyNX0KZ3Nfb2kgPSBjKCJIQUxMTUFSS19JTlRFUkZFUk9OX0FMUEhBX1JFU1BPTlNFIiwKICAgICAgICAgICJIQUxMTUFSS19JTlRFUkZFUk9OX0dBTU1BX1JFU1BPTlNFIiwKICAgICAgICAgICJSRUFDVE9NRV9LRVJBVElOSVpBVElPTiIsCiAgICAgICAgICAiR09CUF9LRVJBVElOSVpBVElPTiIsCiAgICAgICAgICAiR09CUF9NQUNST1BIQUdFX0NZVE9LSU5FX1BST0RVQ1RJT04iLAogICAgICAgICAgIkdPQ0NfTUhDX1BST1RFSU5fQ09NUExFWCIsCiAgICAgICAgICAiS0VHR19BTlRJR0VOX1BST0NFU1NJTkdfQU5EX1BSRVNFTlRBVElPTiIpCgpwbG90X2xpc3QgPSBtYWtlX2dzZWFfcGxvdChnc2VhX3Jlc3VsdHMsIGdzX29pLCBmb2xkX2NoYW5nZSwgIkZDX3hfcGN0IikKCnBhdGNod29yazo6d3JhcF9wbG90cyhwbG90X2xpc3QsIG5jb2wgPSAyLCB3aWR0aHMgPSBjKDIsMS41KSkKYGBgCgpXZSBhdHRyaWJ1dGUgc2NvcmUgZm9yIGVhY2ggZ2VuZSBzZXRzIHRvIGFsbCBjZWxscywgdG8gdmlzdWFsaXplIHRoZSBzY29yZXMgYXMgdmlvbGluIHBsb3RzLgoKYGBge3Igc2NvcmVfZ2VuZV9zZXRzMn0KZm9yIChvbmVfZ2VuZV9zZXQgaW4gZ3Nfb2kpIHsKICBuZXdfY29sdW1uID0gcGFzdGUwKCJzY29yZV8iLCBvbmVfZ2VuZV9zZXQpCiAgCiAgIyBnZW5lIHNldCBjb250ZW50CiAgZ3NfY29udGVudCA9IGdlbmVfc2V0cyAlPiUKICAgIGRwbHlyOjpmaWx0ZXIoZ3NfbmFtZSA9PSBvbmVfZ2VuZV9zZXQpICU+JQogICAgZHBseXI6OnB1bGwoZ2VuZV9zeW1ib2wpICU+JQogICAgdW5saXN0KCkKICAKICAjIGFkZCBzY29yZQogIHNvYmpAbWV0YS5kYXRhWywgbmV3X2NvbHVtbl0gPSBTZXVyYXQ6OkFkZE1vZHVsZVNjb3JlKHNvYmosCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZXMgPSBsaXN0KGdzX2NvbnRlbnQpKSRDbHVzdGVyMQp9CgpoZWFkKHNvYmpAbWV0YS5kYXRhKQpgYGAKCldlIHZpc3VhbGl6ZSB0aGUgc2NvcmVzIGFzIHZpb2xpbiBwbG90cyA6CgpgYGB7ciBzZWVfc2NvcmVfZ3NlYTIsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTJ9CnBsb3RfbGlzdCA9IGxhcHBseShnc19vaSwgRlVOID0gZnVuY3Rpb24ob25lX2dzKSB7CiAgcCA9IFNldXJhdDo6VmxuUGxvdChzb2JqLCBmZWF0dXJlcyA9IHBhc3RlMCgic2NvcmVfIiwgb25lX2dlbmVfc2V0KSwKICAgICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gImNsdXN0ZXJfdHlwZSIsCiAgICAgICAgICAgICAgICAgICAgICBzcGxpdC5ieSA9ICJzYW1wbGVfdHlwZSIpICsKICAgIGdncGxvdDI6OnNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIiNDNTVGNDAiLCAiIzJDNzhFNiIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygiSFMiLCAiSEQiKSkgKwogICAgZ2dwbG90Mjo6bGFicyh0aXRsZSA9IG9uZV9ncykgKwogICAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKQogIAogIHJldHVybihwKQp9KQoKcGF0Y2h3b3JrOjp3cmFwX3Bsb3RzKHBsb3RfbGlzdCwgbmNvbCA9IDIpCmBgYAoKIyBWaXN1YWxpc2F0aW9uCgpXZSByZXByZXNlbnQgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzLiBGaXJzdCwgd2UgZXh0cmFjdCBzb21lIERFIGdlbmVzIDoKCmBgYHtyIHByZXBfZ2VuZXN9CmZlYXR1cmVzX29pID0gbGFwcGx5KGxpc3RfcmVzdWx0cywgYFtbYCwgIm1hcmsiKSAlPiUKICBkby5jYWxsKHJiaW5kLmRhdGEuZnJhbWUsIC4pICU+JQogIGRwbHlyOjpmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSkgJT4lCiAgZHBseXI6OmZpbHRlcihwY3QuMSAtIHBjdC4yID4gMC4yIHwgYWJzKGF2Z19sb2dGQykgPiAxKSAlPiUKICByb3duYW1lcygpICU+JQogIHN0cmluZ3I6OnN0cl9zcGxpdCguLCBwYXR0ZXJuID0gIlxcLiIpICU+JQogIGxhcHBseSguLCBgW1tgLCAyKSAlPiUKICB1bmxpc3QoKSAlPiUKICB1bmlxdWUoKQoKbGVuZ3RoKGZlYXR1cmVzX29pKQpgYGAKCldlIHByZXBhcmUgdGhlIHNjYWxlZCBleHByZXNzaW9uIG1hdHJpeCA6CgpgYGB7ciBtYXR9Cm1hdF9leHByZXNzaW9uID0gU2V1cmF0OjpHZXRBc3NheURhdGEoc29iaiwgYXNzYXkgPSAiUk5BIiwgc2xvdCA9ICJkYXRhIilbZmVhdHVyZXNfb2ksIF0KbWF0X2V4cHJlc3Npb24gPSBNYXRyaXg6OnQobWF0X2V4cHJlc3Npb24pCm1hdF9leHByZXNzaW9uID0gZHludXRpbHM6OnNjYWxlX3F1YW50aWxlKG1hdF9leHByZXNzaW9uKSAjIGJldHdlZW4gMCBhbmQgMQptYXRfZXhwcmVzc2lvbiA9IE1hdHJpeDo6dChtYXRfZXhwcmVzc2lvbikKbWF0X2V4cHJlc3Npb24gPSBhcy5tYXRyaXgobWF0X2V4cHJlc3Npb24pICMgbm90IHNwYXJzZQoKZGltKG1hdF9leHByZXNzaW9uKQpgYGAKCldlIHByZXBhcmUgdGhlIGhlYXRtYXAgYW5ub3RhdGlvbiA6CgpgYGB7ciBoYV90b3B9CmhhX3RvcCA9IENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwQW5ub3RhdGlvbigKICBzYW1wbGVfdHlwZSA9IHNvYmokc2FtcGxlX3R5cGUsCiAgY2x1c3Rlcl90eXBlID0gc29iaiRjbHVzdGVyX3R5cGUsCiAgY2x1c3RlciA9IHNvYmokc2V1cmF0X2NsdXN0ZXJzLAogIGNvbCA9IGxpc3Qoc2FtcGxlX3R5cGUgPSBzZXROYW1lcyhubSA9IGMoIkhTIiwgIkhEIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIiNDNTVGNDAiLCAiIzJDNzhFNiIpKSwKICAgICAgICAgICAgIGNsdXN0ZXJfdHlwZSA9IGNsdXN0ZXJfY29sb3IsCiAgICAgICAgICAgICBjbHVzdGVyID0gc2V0TmFtZXMobm0gPSBsZXZlbHMoc29iaiRzZXVyYXRfY2x1c3RlcnMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFxdWFyaXVzOjpnZ19jb2xvcl9odWUobGVuZ3RoKGxldmVscyhzb2JqJHNldXJhdF9jbHVzdGVycykpKSkpKQpgYGAKCkFuZCB0aGUgaGVhdG1hcCA6CgpgYGB7ciBoZWF0bWFwLCBmaWcud2lkdGggPSAxNSwgZmlnLmhlaWdodCA9IDIwfQpodCA9IENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwKG1hdF9leHByZXNzaW9uLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGFxdWFyaXVzOjpjb2xvcl9jbnYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBBbm5vdGF0aW9uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wX2Fubm90YXRpb24gPSBoYV90b3AsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBHcm91cGluZwogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl9vcmRlciA9IHNvYmpAbWV0YS5kYXRhICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZHBseXI6OmFycmFuZ2UoY2x1c3Rlcl90eXBlLCBzYW1wbGVfdHlwZSwgc2V1cmF0X2NsdXN0ZXJzKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJvd25hbWVzKCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RpdGxlID0gTlVMTCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX3Jvd3MgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHVtbnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbHVtbl9uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVmlzdWFsIGFzcGVjdAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJvcmRlciA9IFRSVUUpCgpDb21wbGV4SGVhdG1hcDo6ZHJhdyhodCwKICAgICAgICAgICAgICAgICAgICAgbWVyZ2VfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgaGVhdG1hcF9sZWdlbmRfc2lkZSA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgICAgICBhbm5vdGF0aW9uX2xlZ2VuZF9zaWRlID0gImJvdHRvbSIpCmBgYAoKIyBTYXZlCgpXZSBzYXZlIHRoZSBsaXN0IG9mIHJlc3VsdHMgOgoKYGBge3Igc2F2ZV9saXN0X3Jlc3VsdHN9CnNhdmVSRFMobGlzdF9yZXN1bHRzLCBmaWxlID0gcGFzdGUwKG91dF9kaXIsICIvIiwgc2F2ZV9uYW1lLCAiX2xpc3RfcmVzdWx0cy5yZHMiKSkKYGBgCgpXZSBzYXZlIHRoZSBTZXVyYXQgb2JqZWN0IHdpdGggdGhlIG5ldyBhbm5vdGF0aW9uIDoKCmBgYHtyIHNhdmVfc29ian0Kc2F2ZVJEUyhzb2JqLCBmaWxlID0gcGFzdGUwKG91dF9kaXIsICIvIiwgc2F2ZV9uYW1lLCAiX3NvYmpfYW5ub3RhdGVkLnJkcyIpKQpgYGAKCgojIFIgU2Vzc2lvbgoKYGBge3Igc2Vzc2lvbmluZm8sIGVjaG8gPSBGQUxTRSwgZm9sZF9vdXRwdXQgPSBUUlVFfQpzZXNzaW9uSW5mbygpCmBgYAoK